[lxc-devel] [lxc/master] Add test of lxc-usernsexec

smoser on Github lxc-bot at linuxcontainers.org
Fri May 29 14:33:43 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 440 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200529/fe9f8b1e/attachment.bin>
-------------- next part --------------
From 8338a3989ee4b357189747bab7a32ed7605cec11 Mon Sep 17 00:00:00 2001
From: Scott Moser <smoser at brickies.net>
Date: Fri, 29 May 2020 10:31:21 -0400
Subject: [PATCH] Add test of lxc-usernsexec

The test executes lxc-usernsexec to create some files and chmod them.
Then makes assertions on the uid and gid of those files from outside.
---
 src/tests/lxc-test-usernsexec | 256 ++++++++++++++++++++++++++++++++++
 1 file changed, 256 insertions(+)
 create mode 100755 src/tests/lxc-test-usernsexec

diff --git a/src/tests/lxc-test-usernsexec b/src/tests/lxc-test-usernsexec
new file mode 100755
index 0000000000..dbde07d50e
--- /dev/null
+++ b/src/tests/lxc-test-usernsexec
@@ -0,0 +1,256 @@
+#!/bin/bash
+#
+# This is a bash test case to test lxc-usernsexec.
+# It basically supports usring lxc-usernsexec to execute itself
+# and then create files and check that their ownership is as expected.
+#
+# It requires that the current user has at least 1 value in subuid and /etc/subgid
+TEMP_D=""
+set -f
+
+fail() { echo "$@" 1>&2; exit 1; }
+error() { echo "$@" 1>&2; }
+
+collect_owners() {
+    # collect_owners([--dir=dir], file1, file2 ...)
+    # set _RET to a space delimited array of
+    #   <file1>:owner:group <file2>:owner:group ...
+    local out="" ret="" dir=""
+    if [ "${1#--dir=}" != "$1" ]; then
+        dir="${1#--dir=}"
+        shift
+    fi
+    for arg in "$@"; do
+        # drop the :* so that input can be same as touch_files.
+        out=$(stat --format "%n:%u:%g" "${dir}${arg}") || {
+            error "failed to stat ${arg}"
+            return 1;
+        }
+        ret="$ret ${out##*/}"
+    done
+    _RET="${ret# }"
+}
+
+cleanup() {
+    if [ -d "$TEMP_D" ]; then
+        rm -Rf "$TEMP_D"
+    fi
+}
+
+touch_files() {
+    # touch_files tok [tok ...]
+    # tok is filename:chown_id:chown_gid
+    # if chown_id or chown_gid is empty, then 
+    local args="" tok="" fname="" uidgid=""
+    args=( "$@" )
+    for tok in "$@"; do
+        fname=${tok%%:*}
+        uidgid=${tok#$fname}
+        uidgid=${uidgid#:}
+        : > "$fname" || { error "failed to create $fname"; return 1; }
+        [ -z "$uidgid" ] && continue
+        chown $uidgid "$fname" || { error "failed to chmod '$uidgid' $fname ($?)"; return 1; }
+    done
+}
+
+inside_cleanup() {
+    local f=""
+    rm -f "${FILES[@]}"
+    echo "$STATUS" >&5
+    echo "$STATUS" >&6
+}
+
+set_files() {
+    local x=""
+    FILES=( )
+    for x in "$@"; do
+        FILES[${#FILES[@]}]="${x%%:*}"
+    done
+}
+
+inside() {
+    # this what gets run inside the usernsexec environment.
+    # basically expects arguments of <filename>:uid:gid
+    # it will create the file, and then chmod it to the provided uid:gid
+    # it writes to file descriptor 5 a single line with space delimited
+    #   exit_value uid gid [<filename>:<owner>:<group> ... ]
+    STATUS=127
+    trap inside_cleanup EXIT
+    local uid="" gid="" x=""
+
+    uid=$(id -u) || fail "failed execution of id -u"
+    gid=$(id -g) || fail "failed execution of id -g"
+
+    set_files "$@"
+
+    touch_files "$@" || fail "failed to create files"
+
+    collect_owners "${FILES[@]}" || fail "failed to collect owners"
+    result="$_RET"
+
+    # tell caller we are done.
+    echo "0" "$uid" "$gid" "$result" >&5
+    STATUS=0
+
+    # let the caller do things while the files are around.
+    read -t 30 x <&6
+
+    exit
+}
+
+runtest() {
+    # runtest(mydir, nsexec_args, [inside [...]])
+    #  - use 'mydir' as a working dir.
+    #  - execute lxc-usernsexec $nsexec_args -- <self> inside <inside args>
+    # 
+    # write to stdout
+    #  exit_value inside_exit_value inside_uid:inside_gid <results>
+    # 
+    # where results are a list of space separated
+    #    filename:uid:gid
+    # for each file passed in inside_args
+    [ $# -ge 3 ] || { error "runtest expects 2 args"; return 1; }
+    local mydir="$1" nsexec_args="$2"
+    shift 2
+    local ret inside_owners t=""
+    KIDPID=""
+
+    mkfifo "${mydir}/5" && exec 5<>"${mydir}/5" || return
+    mkfifo "${mydir}/6" && exec 6<>"${mydir}/6" || return
+    mkdir --mode=777 "${mydir}/work" || return
+    cd "${mydir}/work"
+
+    set_files "$@"
+
+    local results="" oresults="" iresults="" iuid="" igid="" n=0
+
+    error "$" $USERNSEXEC ${nsexec_args} -- "$MYPATH" inside "$*"
+    ${USERNSEXEC} ${nsexec_args} -- "$MYPATH" inside "$@" &
+    KIDPID=$!
+
+    [ -d "/proc/$KIDPID" ] || {
+        wait $KIDPID
+        fail "kid $KIDPID died quickly $?"
+    }
+
+    # if lxc-usernsexec fails to execute MYPATH inside, then
+    # the read below would timeout.  To avoid a long timeout,
+    # we do a short timeout and check the pid is alive.
+    while ! read -t 1 ret iuid igid inside_owners <&5; do
+        n=$((n+1))
+        if [ ! -d "/proc/$KIDPID" ]; then
+            wait $KIDPID
+            fail "kid $KIDPID is gone $?"
+        fi
+        [ $n -ge 30 ] && fail "child never wrote to pipe"
+    done
+    iresults=( $inside_owners )
+
+    collect_owners "--dir=${mydir}/work/" "${FILES[@]}" || return
+    oresults=( $_RET )
+
+    echo 0 >&6
+    wait
+
+    ret=$?
+
+    results=( )
+    for((i=0;i<${#iresults[@]};i++)); do
+        results[$i]="${oresults[$i]}:${iresults[$i]#*:}"
+    done
+
+    echo 0 $ret "$iuid:$igid" "${results[@]}"
+}
+
+runcheck() {
+    local name="$1" expected="$2" nsexec_args="$3" found=""
+    shift 3
+    mkdir "${TEMP_D}/$name" || fail "failed mkdir <TEMP_D>/$name.d"
+    local err="${TEMP_D}/$name.err"
+    out=$("$MYPATH" runtest "${TEMP_D}/$name" "$nsexec_args" "$@" 2>"$err") || {
+        error "$name: FAIL - runtest failed $?"
+        [ -n "$out" ] && error "  $out"
+        sed 's,^,  ,' "$err" 1>&2
+        ERRORS="${ERRORS} $name"
+        return 1
+    }
+    set -- $out
+    local parentrc=$1 kidrc=$2 iuidgid="$3" found=""
+    shift 3
+    found="$*"
+    [ "$parentrc" = "0" -a "$kidrc" = "0" ] || {
+        error "$name: FAIL - parentrc=$parentrc kidrc=$kidrc found=$found"
+        ERRORS="${ERRORS} $name"
+        return 1
+    }
+    [ "$expected" = "$found" ] && {
+        error "$name: PASS"
+        PASS="${PASSES} $name"
+        return 0
+    }
+    echo "$name: FAIL expected '$expected' != found '$found'"
+    FAILS="${FAILS} $name"
+    return 1
+}
+
+USERNSEXEC=${USERNSEXEC:-lxc-usernsexec}
+if [ "$1" = "inside" ]; then
+    shift
+    inside "$@"
+    exit
+elif [ "$1" = "runtest" ]; then
+    shift
+    runtest "$@"
+    exit
+fi
+
+uid=$(id --user) || fail "failed to get uid"
+gid=$(id --group) || fail "failed to get gid"
+name=$(id --user --name) || fail "failed to get username"
+
+subuid=$(awk -F: '$1 == n { print $2; exit(0); }' "n=$name" /etc/subuid) &&
+    [ -n "$subuid" ] || fail "did not find $name in /etc/subuid"
+
+subgid=$(awk -F: '$1 == n { print $2; exit(0); }' "n=$name" /etc/subgid) &&
+    [ -n "$subgid" ] || fail "did not find $name in /etc/subgid"
+
+
+mapuid="u:0:$uid:1"
+mapgid="g:0:$gid:1"
+
+ver=$(dpkg-query --show lxc-utils | awk '{print $2}')
+error "uid=$uid gid=$gid name=$name subuid=$subuid subgid=$subgid ver=$ver"
+error "lxc-utils=$ver kver=$(uname -r)"
+error "USERNSEXEC=$USERNSEXEC"
+
+TEMP_D=$(mktemp -d)
+trap cleanup EXIT
+MYPATH=$(readlink -f "$0") || { echo "failed to get full path to self: $0"; exit 1; }
+export MYPATH
+
+PASSES=""; FAILS=""; ERRORS=""
+runcheck nouidgid  "f0:$subuid:$subgid:0:0" "" f0
+
+runcheck myuidgid  "f0:$uid:$gid:0:0" \
+    "-m$mapuid -m$mapgid" f0
+
+runcheck subuidgid \
+    "f0:$subuid:$subgid:0:0" \
+    "-mu:0:$subuid:1 -mg:0:$subgid:1" f0:0:0
+
+runcheck bothsets  "f0:$uid:$gid:0:0 f1:$subuid:$subgid:1:1 f2:$uid:$subgid:0:1" \
+    "-m$mapuid -m$mapgid -mu:1:$subuid:1 -mg:1:$subgid:1" \
+    f0 f1:1:1 f2::1
+
+runcheck mismatch "f0:$uid:$subgid:0:0 f1:$subuid:$gid:15:31" \
+    "-mu:0:$uid:1 -mg:0:$subgid:1 -mu:15:$subuid:1 -mg:31:$gid:1" \
+    f0 f1:15:31
+
+FAILS=${FAILS# }
+ERRORS=${ERRORS# }
+PASSES=${PASSES# }
+
+[ -z "${FAILS}" ] || error "FAILS: ${FAILS}"
+[ -z "${ERRORS}" ] || error "ERRORS: ${ERRORS}"
+[ -z "${FAILS}" -a -z "${ERRORS}" ] || exit 1
+exit 0


More information about the lxc-devel mailing list