From fabb3e72daca67ba5d00d96dc633c10bd580544c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C5=82awomir=20Nizio?= Date: Sun, 5 Aug 2018 00:34:54 +0200 Subject: [PATCH] [tests] add lib/tests/run-nonpriv-wrapper - runs as non-root - does not require being in entropy/portage group - in fact, it's better (better isolation) to run as such - thus does not modify running system The wrapper script is ugly but very convenient. --- lib/tests/_misc.py | 10 +++- lib/tests/const.py | 1 + lib/tests/run | 21 ++++++++- lib/tests/run-nonpriv-wrapper | 87 +++++++++++++++++++++++++++++++++++ lib/tests/security.py | 2 +- 5 files changed, 117 insertions(+), 4 deletions(-) create mode 100755 lib/tests/run-nonpriv-wrapper diff --git a/lib/tests/_misc.py b/lib/tests/_misc.py index b5423daa7..8d7a97f10 100644 --- a/lib/tests/_misc.py +++ b/lib/tests/_misc.py @@ -2,7 +2,7 @@ import os from entropy.exceptions import FileNotFound -def get_test_generic_package(test_pkg): +def _get_test_generic_package_path(test_pkg): path1 = os.path.join(os.getcwd(), "packages", test_pkg) path2 = os.path.join(os.getcwd(), "..", "packages", test_pkg) if os.path.lexists(path1): @@ -11,6 +11,14 @@ def get_test_generic_package(test_pkg): return path2 raise FileNotFound("cannot find test package %s" % (test_pkg,)) +def get_test_generic_package(test_pkg): + stage_dir = os.getenv("ETP_TESTS_PACKAGES_RW_PATH") + if stage_dir: + path = os.path.join(stage_dir, test_pkg) + else: + path = _get_test_generic_package_path(test_pkg) + return path + def get_test_package(): test_pkg = "zlib-1.2.3-r1.tbz2" return get_test_generic_package(test_pkg) diff --git a/lib/tests/const.py b/lib/tests/const.py index a29a35229..3fa59b712 100644 --- a/lib/tests/const.py +++ b/lib/tests/const.py @@ -12,6 +12,7 @@ from entropy.exceptions import SecurityError class ConstTest(unittest.TestCase): + @unittest.skipIf(os.getenv("ETP_TEST_SKIP_PRIVILEGED"), "ETP_TEST_SKIP_PRIVILEGED is set") def test_privileges(self): self.assertTrue(os.getuid() == 0) diff --git a/lib/tests/run b/lib/tests/run index 6c800c434..63962bd0b 100755 --- a/lib/tests/run +++ b/lib/tests/run @@ -1,6 +1,9 @@ #!/usr/bin/python # -*- coding: utf-8 -*- +# Too run in a (partially) isolated environment and as nonprivileged user, see +# run-nonpriv-wrapper. + import os import sys locale_dir = os.path.realpath(os.path.join(os.getcwd(), "i18n")) @@ -19,8 +22,22 @@ from tests import locks, db, client, server, misc, fetchers, tools, dep, \ i18n, spm, qa, core, security, const # Add to the list the module to test -mods = [locks, db, client, server, misc, fetchers, tools, dep, i18n, spm, qa, - core, security, const] +mods = [ + locks, + db, + client, + server, + misc, + fetchers, + tools, + dep, + i18n, + spm, + qa, + core, + security, + const +] tests = [] for mod in mods: diff --git a/lib/tests/run-nonpriv-wrapper b/lib/tests/run-nonpriv-wrapper new file mode 100755 index 000000000..f7e60f28d --- /dev/null +++ b/lib/tests/run-nonpriv-wrapper @@ -0,0 +1,87 @@ +#!/bin/bash + +# This is a wrapper that sets environment for running Entropy tests as +# unprivileged user (including not in entropy/portage group), and without +# altering the running system. +# Because the environment is modified (or artificial, e.g. some directories are +# empty), it might be possible that some code paths don't execute as intended. +# If you enounter this, file a bug. + +# Requires bubblewrap (https://github.com/projectatomic/bubblewrap). + +# Usage: +# ./nonpriv-wrapper.sh +# will call ./run.sh +# ./nonpriv-wrapper.sh COMMAND [ARG ...] +# will run COMMAND in modified environment + +this_dir=$(dirname "$0") || exit 1 + +cmd=( "$this_dir/run" ) +(( $# )) && cmd=( "$@" ) + +# Note: cannot be on a nonexec-mounted fs due to ldd -r xxx.so. +test_dir=/var/tmp/tdirs + +rm -rf "$test_dir" +mkdir -p "$test_dir"/{var-lib-entropy,var-tmp-entropy,run,etc-entropy} +mkdir -p "$test_dir"/{var-db,var-lib-portage} + +# For os.chown. +# For is_user_in_entropy_group(). +grep -E -v "^(entropy|portage):" /etc/group > "$test_dir/etc-group" +echo "entropy:x:$(id -g):$(whoami)" >> "$test_dir/etc-group" +echo "portage:x:$(id -g):" >> "$test_dir/etc-group" + +# Checkout of Entropy can be ro. Create place for writable files normally used +# just from the "packages" directory (get_test_generic_package, _misc.py). +mkdir -p "$test_dir/var-tmp-entropy/test-packages-rw" +cp -r "$this_dir/packages"/* "$test_dir/var-tmp-entropy/test-packages-rw" + +# For the case when os.chown is being done on this file. +printf "product = standard\nbranch = 5\nofficial-repository-id = sabayonlinux.org\n" \ + > "$test_dir/etc-entropy/repositories.conf" + +# For test_clear_cache (tests.client.EntropyClientTest). +mkdir "$test_dir/var-lib-entropy/caches" +echo garbage > "$test_dir/var-lib-entropy/caches/garbage-file" + +# For test_basic_methods (tests.spm.SpmTest). +mkdir -p "$test_dir/var-db/pkg/sys-apps/gawk-4.2.0" +mkdir -p "$test_dir/var-db/pkg/virtual/libc-1" +mkdir -p "$test_dir/var-db/pkg/virtual/editor-0-r1" + +# For tests.security.SecurityTest.test_gpg_handling. +mkdir -p "$test_dir/var-lib-entropy/client/database/amd64" + +# Some of these are .example, not real files but are good enough. +# For security.SecurityTest.test_security_cache. +cp -r "$this_dir/../../conf"/* "$test_dir/etc-entropy" +mkdir "$test_dir/etc-entropy/packages/sets" + +realtowrapped() { + echo "$test_dir/$(echo "$1" | sed -e "s:^/::" -e "s:/:-:g")" +} + +opts=( +--ro-bind / / +--dev /dev +--bind /tmp /tmp +--proc /proc +--bind "$(realtowrapped /var/lib/entropy)" /var/lib/entropy +--bind "$(realtowrapped /var/tmp/entropy)" /var/tmp/entropy +--bind "$(realtowrapped /run)" /run +--bind "$(realtowrapped /etc/entropy)" /etc/entropy + +--bind "$(realtowrapped /var/db)" /var/db +--bind "$(realtowrapped /var/lib/portage)" /var/lib/portage + +--bind "$(realtowrapped /etc/group)" /etc/group +) + +export ETP_TESTS_PACKAGES_RW_PATH="/var/tmp/entropy/test-packages-rw" + +# Disable a test that explicitly requires root (check os.getuid()). +export ETP_TEST_SKIP_PRIVILEGED=1 + +exec bwrap "${opts[@]}" "${cmd[@]}" diff --git a/lib/tests/security.py b/lib/tests/security.py index 230e24e28..b5ad36eeb 100644 --- a/lib/tests/security.py +++ b/lib/tests/security.py @@ -26,7 +26,7 @@ class SecurityTest(unittest.TestCase): self._entropy = Client(installed_repo = False) self._repository = Repository(keystore_dir = self._tmp_dir) - tmp_dir = os.getenv("TMPDIR", os.getcwd()) + tmp_dir = os.getenv("TMPDIR", etpConst['entropyunpackdir']) self._security_cache_dir = const_mkdtemp( dir=tmp_dir, prefix="entropy.SecurityTest") self._security_dir = const_mkdtemp(