diff --git a/README.md b/README.md index 321feae..72ae100 100644 --- a/README.md +++ b/README.md @@ -110,9 +110,9 @@ used, see the [Usage](#usage) section below. Run JuNest installed programs directly from host OS --------------------------------------- -Program installed within JuNest can be accessible directly from host machine -without entering directly into a JuNest session -(no need to call `junest` command first). +Programs installed within JuNest can be accessible directly from host machine +without entering into a JuNest session +(namely, no need to call `junest` command first). For instance, supposing the host OS is an Ubuntu distro you can directly run `pacman` by simply updating the `PATH` variable: @@ -123,7 +123,7 @@ htop ``` By default the wrappers use `ns` mode. To use the `ns --fakeroot` you can use the convenient command helper `sudoj`. -For more control on backend mode you can use the `JUNEST_ARGS` environment variable. +For more control on backend modes you can use the `JUNEST_ARGS` environment variable too. For instance, if you want to run `iftop` with real root privileges: ``` @@ -138,6 +138,22 @@ corrupted) with: junest create-bin-wrappers -f ``` +Bin wrappers are automatically generated each time they get installed inside JuNest. +This only works for executables located in `/usr/bin` path. +For executables in other locations (say `/usr/mybinpath`) you can only create +wrappers manually by executing the command: + +``` +junest create-bin-wrappers --bin-path /usr/mybinpath +``` + +Obviously, to get access to the corresponding bin wrappers you will need to +update your `PATH` variable accordingly: + +``` +export PATH="$PATH:~/.junest/usr/mybinpath_wrappers" +``` + Install packages from AUR ------------------------- @@ -149,14 +165,11 @@ command. In `proot` mode, JuNest does no longer support the building of AUR pack first: ```sh -pacman -Syu --ignore sudo base-devel -:: sudo is in IgnorePkg/IgnoreGroup. Install anyway? [Y/n] n -... -... +pacman -S base-devel ``` -JuNest uses a modified version of `sudo`. That's why the original `sudo` -package **must be ignored** in the previous command. +JuNest uses a modified version of `sudo` provided by `junest/sudo-fake`. And the original `core/sudo` +package will be ignored **(and must not be installed)** during the installation of `base-devel`. Have fun! --------- @@ -387,9 +400,9 @@ For Arch Linux related FAQs take a look at the [General troubleshooting page](ht > In order to install AUR packages you need to install the package group `base-devel` first > that contains all the essential packages for compiling from source code (such as gcc, make, patch, etc): - #> pacman -S --ignore sudo base-devel + #> pacman -S base-devel -> Remember to ignore `sudo` as it conflicts with `sudo-fake` package. +> Remember to not install `core/sudo` as it conflicts with `junest/sudo-fake` package. ## Can't set user and group as root diff --git a/VERSION b/VERSION index 0f4a1d6..25627bc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.4.3 +7.4.7 diff --git a/bin/junest b/bin/junest index d3435fe..f0d6abe 100755 --- a/bin/junest +++ b/bin/junest @@ -69,8 +69,11 @@ usage() { echo -e " b[uild] Build a $NAME image (must run in ArchLinux)" echo -e " -n, --disable-check Disable the $NAME image check" echo - echo -e " create-bin-wrappers Create bin wrappers in $JUNEST_HOME/usr/bin_wrappers" + echo -e " create-bin-wrappers Create a bin wrappers directory according to --bin-path option" + echo -e " Default path is $JUNEST_HOME/usr/bin_wrappers" echo -e " -f, --force Create the wrapper files even if they already exist" + echo -e " -p, --bin-path The source directory where executable are located in JuNest" + echo -e " Default value is: /usr/bin" echo } @@ -215,10 +218,12 @@ function _parse_build_opts() { function _parse_create_wrappers_opts() { OPT_FORCE=false + OPT_BIN_PATH="" while [[ -n "$1" ]] do case "$1" in -f|--force) OPT_FORCE=true ; shift ;; + -p|--bin-path) shift ; OPT_BIN_PATH="$1" ; shift ;; *) die "Invalid option $1" ;; esac done @@ -276,7 +281,7 @@ function execute_operation() { fi if $ACT_CREATE_WRAPPERS; then - create_wrappers $OPT_FORCE + create_wrappers $OPT_FORCE "$OPT_BIN_PATH" exit fi @@ -300,7 +305,8 @@ function execute_operation() { fi # Call create_wrappers in case new bin files have been created - trap "create_wrappers" EXIT QUIT TERM + # shellcheck disable=SC2064 + trap "PATH=$PATH create_wrappers" EXIT QUIT TERM $run_env "$BACKEND_COMMAND" "${BACKEND_ARGS}" $OPT_NO_COPY_FILES "${ARGS[@]}" } diff --git a/lib/checks/check_all.sh b/lib/checks/check_all.sh index 2b39f9a..13e9237 100755 --- a/lib/checks/check_all.sh +++ b/lib/checks/check_all.sh @@ -24,4 +24,8 @@ sudo -E "$JUNEST_SCRIPT" groot -- "$CHECK_SCRIPT" --run-root-tests --skip-aur-te # Test the wrappers work "$JUNEST_SCRIPT" create-bin-wrappers --force "$JUNEST_HOME"/usr/bin_wrappers/pacman --help + +"$JUNEST_SCRIPT" create-bin-wrappers --force --bin-path /usr/bin/core_perl/ +"$JUNEST_HOME"/usr/bin/core_perl_wrappers/shasum --help + "${JUNEST_BASE}/bin/sudoj" pacman -Syu diff --git a/lib/core/namespace.sh b/lib/core/namespace.sh index 9471f31..70763bd 100644 --- a/lib/core/namespace.sh +++ b/lib/core/namespace.sh @@ -15,8 +15,24 @@ COMMON_BWRAP_OPTION="--bind "$JUNEST_HOME" / --bind "$HOME" "$HOME" --bind /tmp CONFIG_PROC_FILE="/proc/config.gz" CONFIG_BOOT_FILE="/boot/config-$($UNAME -r)" PROC_USERNS_CLONE_FILE="/proc/sys/kernel/unprivileged_userns_clone" +PROC_USERNS_FILE="/proc/$$/ns/user" function _is_user_namespace_enabled() { + if [[ -L $PROC_USERNS_FILE ]] + then + return 0 + fi + + if [[ -e $PROC_USERNS_CLONE_FILE ]] + then + # `-q` option in zgrep may cause a gzip: stdout: Broken pipe + # Use redirect to /dev/null instead + if zgrep_cmd "1" "$PROC_USERNS_CLONE_FILE" > /dev/null + then + return 0 + fi + fi + local config_file="" if [[ -e $CONFIG_PROC_FILE ]] then @@ -35,19 +51,7 @@ function _is_user_namespace_enabled() { return "$NO_CONFIG_FOUND" fi - if [[ ! -e $PROC_USERNS_CLONE_FILE ]] - then - return 0 - fi - - # `-q` option in zgrep may cause a gzip: stdout: Broken pipe - # Use redirect to /dev/null instead - if ! zgrep_cmd "1" $PROC_USERNS_CLONE_FILE > /dev/null - then - return "$UNPRIVILEGED_USERNS_DISABLED" - fi - - return 0 + return "$UNPRIVILEGED_USERNS_DISABLED" } function _check_user_namespace() { @@ -101,8 +105,9 @@ function run_env_as_bwrap_fakeroot(){ local args=() [[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")") + # Fix PATH to /usr/bin to make sudo working and avoid polluting with host related bin paths # shellcheck disable=SC2086 - BWRAP="${backend_command}" JUNEST_ENV=1 bwrap_cmd $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 $backend_args sudo "${DEFAULT_SH[@]}" "${args[@]}" + PATH="/usr/bin" BWRAP="${backend_command}" JUNEST_ENV=1 bwrap_cmd $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 $backend_args sudo "${DEFAULT_SH[@]}" "${args[@]}" } @@ -150,8 +155,9 @@ function run_env_as_bwrap_user() { local args=() [[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")") + # Resets PATH to avoid polluting with host related bin paths # shellcheck disable=SC2086 - BWRAP="${backend_command}" JUNEST_ENV=1 bwrap_cmd $COMMON_BWRAP_OPTION $backend_args "${DEFAULT_SH[@]}" "${args[@]}" + PATH='' BWRAP="${backend_command}" JUNEST_ENV=1 bwrap_cmd $COMMON_BWRAP_OPTION $backend_args "${DEFAULT_SH[@]}" "${args[@]}" } diff --git a/lib/core/proot.sh b/lib/core/proot.sh index 259c8b7..b6c1c8f 100644 --- a/lib/core/proot.sh +++ b/lib/core/proot.sh @@ -17,7 +17,8 @@ function _run_env_with_proot(){ local args=() [[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")") - PROOT="${backend_command}" JUNEST_ENV=1 proot_cmd "${backend_args}" "${DEFAULT_SH[@]}" "${args[@]}" + # Resets PATH to avoid polluting with host related bin paths + PATH='' PROOT="${backend_command}" JUNEST_ENV=1 proot_cmd "${backend_args}" "${DEFAULT_SH[@]}" "${args[@]}" } function _run_env_with_qemu(){ diff --git a/lib/core/wrappers.sh b/lib/core/wrappers.sh index 1afbb2b..1fe955c 100644 --- a/lib/core/wrappers.sh +++ b/lib/core/wrappers.sh @@ -20,7 +20,9 @@ ####################################### function create_wrappers() { local force=${1:-false} - mkdir -p "${JUNEST_HOME}/usr/bin_wrappers" + local bin_path=${2:-/usr/bin} + bin_path=${bin_path%/} + mkdir -p "${JUNEST_HOME}${bin_path}_wrappers" # Arguments inside a variable (i.e. `JUNEST_ARGS`) separated by quotes # are not recognized normally unless using `eval`. More info here: # https://github.com/fsquillace/junest/issues/262 @@ -33,26 +35,26 @@ junest "\${junest_args_array[@]}" -- \$(basename \${0}) "\$@" EOF chmod +x "${JUNEST_HOME}/usr/bin/junest_wrapper" - cd "${JUNEST_HOME}/usr/bin" || return 1 + cd "${JUNEST_HOME}${bin_path}" || return 1 for file in * do [[ -d $file ]] && continue - # Symlinks outside junest appear as broken even though the are correct + # Symlinks outside junest appear as broken even though they are correct # within a junest session. The following do not skip broken symlinks: [[ -x $file || -L $file ]] || continue - if [[ -e ${JUNEST_HOME}/usr/bin_wrappers/$file ]] && ! $force + if [[ -e ${JUNEST_HOME}${bin_path}_wrappers/$file ]] && ! $force then continue fi - rm -f "${JUNEST_HOME}/usr/bin_wrappers/$file" - ln -s "../bin/junest_wrapper" "${JUNEST_HOME}/usr/bin_wrappers/$file" + rm -f "${JUNEST_HOME}${bin_path}_wrappers/$file" + ln -s "${JUNEST_HOME}/usr/bin/junest_wrapper" "${JUNEST_HOME}${bin_path}_wrappers/$file" done # Remove wrappers no longer needed - cd "${JUNEST_HOME}/usr/bin_wrappers" || return 1 + cd "${JUNEST_HOME}${bin_path}_wrappers" || return 1 for file in * do - [[ -e ${JUNEST_HOME}/usr/bin/$file || -L ${JUNEST_HOME}/usr/bin/$file ]] || rm -f "$file" + [[ -e ${JUNEST_HOME}${bin_path}/$file || -L ${JUNEST_HOME}${bin_path}/$file ]] || rm -f "$file" done } diff --git a/tests/unit-tests/test-namespace.sh b/tests/unit-tests/test-namespace.sh index 373c1be..781ca92 100755 --- a/tests/unit-tests/test-namespace.sh +++ b/tests/unit-tests/test-namespace.sh @@ -58,50 +58,45 @@ function _test_copy_remaining_files() { } function test_is_user_namespace_enabled_no_config_file(){ + PROC_USERNS_FILE="blah" + PROC_USERNS_CLONE_FILE="blah" CONFIG_PROC_FILE="blah" CONFIG_BOOT_FILE="blah" assertCommandFailOnStatus "$NOT_EXISTING_FILE" _is_user_namespace_enabled } function test_is_user_namespace_enabled_no_config(){ + PROC_USERNS_FILE="blah" + PROC_USERNS_CLONE_FILE="blah" touch config gzip config + # shellcheck disable=SC2034 CONFIG_PROC_FILE="config.gz" + # shellcheck disable=SC2034 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" - PROC_USERNS_CLONE_FILE="not-existing-file" - assertCommandSuccess _is_user_namespace_enabled -} - function test_is_user_namespace_enabled_with_userns_clone_file_disabled(){ - echo "CONFIG_USER_NS=y" > config - gzip config - CONFIG_PROC_FILE="config.gz" - CONFIG_BOOT_FILE="blah" + PROC_USERNS_FILE="blah" PROC_USERNS_CLONE_FILE="unprivileged_userns_clone" echo "0" > $PROC_USERNS_CLONE_FILE assertCommandFailOnStatus "$UNPRIVILEGED_USERNS_DISABLED" _is_user_namespace_enabled } function test_is_user_namespace_enabled_with_userns_clone_file_enabled(){ - echo "CONFIG_USER_NS=y" > config - gzip config - # shellcheck disable=SC2034 - CONFIG_PROC_FILE="config.gz" - # shellcheck disable=SC2034 - CONFIG_BOOT_FILE="blah" PROC_USERNS_CLONE_FILE="unprivileged_userns_clone" echo "1" > $PROC_USERNS_CLONE_FILE assertCommandSuccess _is_user_namespace_enabled } +function test_is_user_namespace_enabled_with_proc_userns_file_existing(){ + PROC_USERNS_FILE="user" + ln -s . $PROC_USERNS_FILE + PROC_USERNS_CLONE_FILE="blah" + assertCommandSuccess _is_user_namespace_enabled +} + function test_run_env_as_bwrap_fakeroot() { assertCommandSuccess run_env_as_bwrap_fakeroot "" "" "false" assertEquals "$BWRAP $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 sudo /bin/sh --login" "$(cat "$STDOUTF")" diff --git a/tests/unit-tests/test-wrappers.sh b/tests/unit-tests/test-wrappers.sh index c5a5857..eef1f38 100755 --- a/tests/unit-tests/test-wrappers.sh +++ b/tests/unit-tests/test-wrappers.sh @@ -124,4 +124,15 @@ function test_create_wrappers_executable_no_longer_exist(){ assertTrue "myfile wrapper should not exist" "[ ! -x $JUNEST_HOME/usr/bin_wrappers/myfile ]" } +function test_create_wrappers_custom_bin_path(){ + mkdir -p "$JUNEST_HOME"/usr/mybindir + touch "$JUNEST_HOME"/usr/mybindir/myfile + chmod +x "$JUNEST_HOME"/usr/mybindir/myfile + assertCommandSuccess create_wrappers false /usr/mybindir/ + assertEquals "" "$(cat "$STDOUTF")" + assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/mybindir_wrappers ]" + assertTrue "myfile wrapper should exist" "[ -x $JUNEST_HOME/usr/mybindir_wrappers/myfile ]" +} + + source "$(dirname "$0")"/../utils/shunit2