From 0ec35a408800a9f1a858d983d3c1ab3024682c26 Mon Sep 17 00:00:00 2001 From: Filippo Squillace Date: Tue, 14 Mar 2017 23:43:52 +0000 Subject: [PATCH] Issue #174: Add namespace module and check for user namespace --- bin/junest | 43 ++++++++++++++++++-------- lib/core/common.sh | 7 +++++ lib/core/namespace.sh | 47 +++++++++++++++++++++++++++++ tests/unit-tests/test-cli.sh | 43 +++++++++++++++++++++++--- tests/unit-tests/test-common.sh | 10 +++++++ tests/unit-tests/test-namespace.sh | 48 ++++++++++++++++++++++++++++++ 6 files changed, 181 insertions(+), 17 deletions(-) create mode 100644 lib/core/namespace.sh create mode 100755 tests/unit-tests/test-namespace.sh diff --git a/bin/junest b/bin/junest index 836c19b..5e2e6bf 100755 --- a/bin/junest +++ b/bin/junest @@ -7,10 +7,13 @@ JUNEST_BASE="$(readlink -f $(dirname $(readlink -f "$0"))/..)" source "${JUNEST_BASE}/lib/utils/utils.sh" source "${JUNEST_BASE}/lib/core/common.sh" + source "${JUNEST_BASE}/lib/core/build.sh" -source "${JUNEST_BASE}/lib/core/setup.sh" -source "${JUNEST_BASE}/lib/core/proot.sh" source "${JUNEST_BASE}/lib/core/chroot.sh" +source "${JUNEST_BASE}/lib/core/namespace.sh" +source "${JUNEST_BASE}/lib/core/proot.sh" +source "${JUNEST_BASE}/lib/core/setup.sh" + ################################### ### General functions ### @@ -29,8 +32,11 @@ usage() { echo echo -e "Access options:" echo -e "-f, --fakeroot Run $NAME with fakeroot privileges" - echo -e "-r, --root Run $NAME with root privileges" - echo -e "-p, --proot-args Proot arguments (use $CMD -p \"--help\" to check out the proot options)" + echo -e "-r, --root Run $NAME with root privileges via jchroot" + echo -e "-p, --backend-args Arguments for backend program (PRoot or jchroot)" + echo -e " ($CMD -p \"--help\" to check out the PRoot options" + echo -e " $CMD -u -p \"--help\" to check out the jchroot options)" + echo -e "-u, --user-namespace Use Linux User Namespace instead of PRoot" echo echo -e "Building options:" echo -e "-b, --build-image Build a $NAME image (must run in ArchLinux)" @@ -109,7 +115,7 @@ check_cli(){ then die "You must access to $NAME with either fakeroot or root permissions" fi - if $OPT_PROOT_ARGS || $OPT_ARCH + if $OPT_BACKEND_ARGS || $OPT_ARCH then if $OPT_BUILD_IMAGE || $OPT_DELETE || $OPT_HELP || \ $OPT_ROOT || $OPT_VERSION || $OPT_DISABLE_VALIDATION || $OPT_CHECK @@ -135,8 +141,9 @@ function parse_arguments(){ IMAGE_FILE="" OPT_FAKEROOT=false OPT_ROOT=false - OPT_PROOT_ARGS=false - PROOT_ARGS="" + OPT_USER_NAMESPACE=false + OPT_BACKEND_ARGS=false + BACKEND_ARGS="" OPT_ARCH=false ARCH_ARG="" OPT_BUILD_IMAGE=false @@ -153,7 +160,8 @@ function parse_arguments(){ -i|--setup-from-file) OPT_SETUP_FROM_FILE=true ; shift ; IMAGE_FILE=$1 ; shift ;; -f|--fakeroot) OPT_FAKEROOT=true ; shift ;; -r|--root) OPT_ROOT=true ; shift ;; - -p|--proot-args) OPT_PROOT_ARGS=true ; shift ; PROOT_ARGS=$1; shift ;; + -u|--user-namespace) OPT_USER_NAMESPACE=true ; shift ;; + -p|--backend-args) OPT_BACKEND_ARGS=true ; shift ; BACKEND_ARGS=$1; shift ;; -a|--arch) OPT_ARCH=true ; shift ; ARCH_ARG=$1; shift ;; -b|--build-image) OPT_BUILD_IMAGE=true ; shift ;; -n|--disable-validation) OPT_DISABLE_VALIDATION=true ; shift ;; @@ -205,13 +213,22 @@ function execute_operation(){ [ -z "${ARCH_ARG}" ] || \ die "The option --arch cannot be specified since JuNest has already been downloaded in $JUNEST_HOME" - if $OPT_FAKEROOT; then - run_env_as_fakeroot "${PROOT_ARGS}" "${ARGS[@]}" - elif $OPT_ROOT; then - run_env_as_root "${ARGS[@]}" + if $OPT_USER_NAMESPACE; then + if $OPT_FAKEROOT; then + run_env_as_fakeroot_with_namespace "${BACKEND_ARGS}" "${ARGS[@]}" + else + run_env_as_user_with_namespace "${BACKEND_ARGS}" "${ARGS[@]}" + fi else - run_env_as_user "${PROOT_ARGS}" "${ARGS[@]}" + if $OPT_FAKEROOT; then + run_env_as_fakeroot "${BACKEND_ARGS}" "${ARGS[@]}" + elif $OPT_ROOT; then + run_env_as_root "${ARGS[@]}" + else + run_env_as_user "${BACKEND_ARGS}" "${ARGS[@]}" + fi fi + } function cli() { diff --git a/lib/core/common.sh b/lib/core/common.sh index d317933..1ceb092 100644 --- a/lib/core/common.sh +++ b/lib/core/common.sh @@ -19,6 +19,7 @@ ARCHITECTURE_MISMATCH=104 ROOT_ACCESS_ERROR=105 NESTED_ENVIRONMENT=106 VARIABLE_NOT_SET=107 +NO_CONFIG_FOUND=108 if [ "$JUNEST_ENV" == "1" ] then @@ -89,6 +90,8 @@ RM=rm MKDIR=mkdir GETENT=getent CP=cp +# Used for checking user namespace in config.gz file +ZGREP=zgrep LD_EXEC="$LD_LIB --library-path ${JUNEST_HOME}/usr/lib:${JUNEST_HOME}/lib" @@ -120,6 +123,10 @@ function mkdir_cmd(){ $MKDIR $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$MKDIR $@ } +function zgrep_cmd(){ + $ZGREP $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/$ZGREP $@ +} + function proot_cmd(){ local proot_args="$1" shift diff --git a/lib/core/namespace.sh b/lib/core/namespace.sh new file mode 100644 index 0000000..ee26d6c --- /dev/null +++ b/lib/core/namespace.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# +# This module contains all namespace functionalities for JuNest. +# +# http://man7.org/linux/man-pages/man7/namespaces.7.html +# http://man7.org/linux/man-pages/man2/unshare.2.html +# +# Dependencies: +# - lib/utils/utils.sh +# - lib/core/common.sh +# +# vim: ft=sh + +CONFIG_PROC_FILE="/proc/config.gz" +CONFIG_BOOT_FILE="/boot/config-$($UNAME -r)" + +function _is_user_namespace_enabled() { + local config_file="" + if [[ -e $CONFIG_PROC_FILE ]] + then + config_file=$CONFIG_PROC_FILE + elif [[ -e $CONFIG_BOOT_FILE ]] + then + config_file=$CONFIG_BOOT_FILE + else + return $NOT_EXISTING_FILE + fi + + if ! zgrep_cmd "CONFIG_USER_NS=y" $config_file + then + return $NO_CONFIG_FOUND + fi +} + +function run_env_as_user_with_namespace() { + set +e + _is_user_namespace_enabled + case $? in + $NOT_EXISTING_FILE) warn "Could not understand if user namespace is enabled. No config.gz file found. Proceeding anyway..." ;; + $NO_CONFIG_FOUND) warn "User namespace is not enabled or Kernel too old. Proceeding anyway..." ;; + esac + set -e +} + +function run_env_as_fakeroot_with_namespace() { + die_on_status 1 "Not implemented yet" +} diff --git a/tests/unit-tests/test-cli.sh b/tests/unit-tests/test-cli.sh index db23eea..efe55b5 100755 --- a/tests/unit-tests/test-cli.sh +++ b/tests/unit-tests/test-cli.sh @@ -44,17 +44,27 @@ function setup_env(){ echo "setup_env($1)" } function run_env_as_fakeroot(){ - local proot_args="$1" + local backend_args="$1" shift - echo "run_env_as_fakeroot($proot_args,$@)" + echo "run_env_as_fakeroot($backend_args,$@)" } function run_env_as_root(){ echo "run_env_as_root $@" } function run_env_as_user(){ - local proot_args="$1" + local backend_args="$1" shift - echo "run_env_as_user($proot_args,$@)" + echo "run_env_as_user($backend_args,$@)" +} +function run_env_as_fakeroot_with_namespace(){ + local backend_args="$1" + shift + echo "run_env_as_fakeroot_with_namespace($backend_args,$@)" +} +function run_env_as_user_with_namespace(){ + local backend_args="$1" + shift + echo "run_env_as_user_with_namespace($backend_args,$@)" } function test_help(){ @@ -164,6 +174,31 @@ function test_run_env_as_root(){ assertEquals "run_env_as_root command" "$(cat $STDOUTF)" } +function test_run_env_as_fakeroot_with_namespace(){ + assertCommandSuccess cli -u -f + assertEquals "run_env_as_fakeroot_with_namespace(,)" "$(cat $STDOUTF)" + assertCommandSuccess cli --user-namespace --fakeroot + assertEquals "run_env_as_fakeroot_with_namespace(,)" "$(cat $STDOUTF)" + + assertCommandSuccess cli -u -f -p "-b arg" + assertEquals "run_env_as_fakeroot_with_namespace(-b arg,)" "$(cat $STDOUTF)" + assertCommandSuccess cli -u -f -p "-b arg" -- command -kv + assertEquals "run_env_as_fakeroot_with_namespace(-b arg,command -kv)" "$(cat $STDOUTF)" + assertCommandSuccess cli -u -f command --as + assertEquals "run_env_as_fakeroot_with_namespace(,command --as)" "$(cat $STDOUTF)" +} +function test_run_env_as_user_with_namespace(){ + assertCommandSuccess cli -u + assertEquals "run_env_as_user_with_namespace(,)" "$(cat $STDOUTF)" + + assertCommandSuccess cli -u -p "-b arg" + assertEquals "run_env_as_user_with_namespace(-b arg,)" "$(cat $STDOUTF)" + assertCommandSuccess cli -u -p "-b arg" -- command -ll + assertEquals "run_env_as_user_with_namespace(-b arg,command -ll)" "$(cat $STDOUTF)" + assertCommandSuccess cli -u command -ls + assertEquals "run_env_as_user_with_namespace(,command -ls)" "$(cat $STDOUTF)" +} + function test_check_cli(){ assertCommandFail cli -b -h assertCommandFail cli -b -c diff --git a/tests/unit-tests/test-common.sh b/tests/unit-tests/test-common.sh index ad871f5..3cbed72 100755 --- a/tests/unit-tests/test-common.sh +++ b/tests/unit-tests/test-common.sh @@ -100,6 +100,16 @@ function test_mkdir(){ MKDIR=false LD_EXEC=false assertCommandFail mkdir_cmd -p new_dir/new_dir } +function test_zgrep(){ + ZGREP=echo assertCommandSuccess zgrep_cmd new_file + assertEquals "new_file" "$(cat $STDOUTF)" + + ZGREP=false assertCommandSuccess zgrep_cmd new_file + assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false new_file" "$(cat $STDOUTF)" + + ZGREP=false LD_EXEC=false assertCommandFail zgrep_cmd new_file +} + function test_chroot(){ CHROOT=echo assertCommandSuccess chroot_cmd root assertEquals "root" "$(cat $STDOUTF)" diff --git a/tests/unit-tests/test-namespace.sh b/tests/unit-tests/test-namespace.sh new file mode 100755 index 0000000..deb90d2 --- /dev/null +++ b/tests/unit-tests/test-namespace.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +JUNEST_ROOT=$(readlink -f $(dirname $0)/../..) + +source "$JUNEST_ROOT/tests/utils/utils.sh" + +source "$JUNEST_ROOT/lib/utils/utils.sh" +source "$JUNEST_ROOT/lib/core/common.sh" +source "$JUNEST_ROOT/lib/core/namespace.sh" + +# Disable the exiterr +set +e + +function oneTimeSetUp(){ + setUpUnitTests +} + +function setUp(){ + cwdSetUp +} + +function tearDown(){ + cwdTearDown +} + +function test_is_user_namespace_enabled_no_config_file(){ + CONFIG_PROC_FILE="blah" + CONFIG_BOOT_FILE="blah" + assertCommandFailOnStatus $NOT_EXISTING_FILE _is_user_namespace_enabled +} + +function test_is_user_namespace_enabled_no_config(){ + touch config + gzip config + CONFIG_PROC_FILE="config.gz" + CONFIG_BOOT_FILE="blah" + assertCommandFailOnStatus $NO_CONFIG_FOUND _is_user_namespace_enabled +} + +function test_is_user_namespace_enabled_with_config(){ + echo "CONFIG_USER_NS=y" > config + gzip config + CONFIG_PROC_FILE="config.gz" + CONFIG_BOOT_FILE="blah" + assertCommandSuccess _is_user_namespace_enabled +} + +source $JUNEST_ROOT/tests/utils/shunit2