|
#!/bin/bash |
|
TEMP_D="" |
|
KID="" |
|
MYNAME="" |
|
VERBOSITY=${_VERBOSITY:-0} |
|
|
|
cleanup() { |
|
[ -d "${TEMP_D}" ] && rm -Rf "${TEMP_D}" |
|
} |
|
|
|
error() { echo "$@" 1>&2 ; } |
|
fail() { |
|
[ $# -eq 0 ] || error "$@" |
|
exit 1 |
|
} |
|
|
|
kidmode() { |
|
local x |
|
read x <&9 |
|
[ "$x" = "1" ] || fail "$$ read $x from 3" |
|
debug 2 "$$ moving on" |
|
exec 9<&- |
|
exec "$@" |
|
} |
|
|
|
Usage() { |
|
cat <<EOF |
|
Usage: ${0##*/} [ mappings ] <CMD> <<ARGUMENTS>> |
|
|
|
Apply mappings and execute CMD with arguments. |
|
|
|
options: |
|
-m <uid-maps> uid maps to use. |
|
EOF |
|
} |
|
|
|
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; return 1; } |
|
|
|
debug() { |
|
local level=${1}; shift; |
|
[ "${level}" -gt "${VERBOSITY}" ] && return |
|
error "${@}" |
|
} |
|
|
|
remap() { |
|
local prog="$1" pid="$2" r="" args="" group="" |
|
shift 2 |
|
if [ $# -eq 0 ]; then |
|
debug 1 "no maps for $prog" |
|
return 0 |
|
fi |
|
# maps are ns_id:host_id:range |
|
# new[ug]idmap want <uid> <loweruid> <count> |
|
args=( ) |
|
for group in "$@"; do |
|
args=( "${args[@]}" ${group//:/ } ) |
|
done |
|
debug 2 "$prog $pid ${args[@]}" |
|
$prog $pid "${args[@]}" && return 0 |
|
r=$? |
|
error "failed [$r]: $prog $pid ${args[*]}" |
|
return $r |
|
} |
|
|
|
check_map() { |
|
# just validate quickly |
|
local t=${1%%:*} |
|
case "$t" in |
|
u|g|b) :;; |
|
*) return 1;; |
|
esac |
|
} |
|
|
|
main() { |
|
local short_opts="hm:Nv" |
|
local long_opts="help,no-defaults,verbose" |
|
local getopt_out="" |
|
getopt_out=$(getopt --name "${0##*/}" \ |
|
--options "${short_opts}" --long "${long_opts}" -- "$@") && |
|
eval set -- "${getopt_out}" || |
|
{ bad_Usage; return; } |
|
|
|
local cur="" next="" umaps="" gmaps="" defmaps=true |
|
|
|
umaps=( ); |
|
gmaps=( ) |
|
|
|
while [ $# -ne 0 ]; do |
|
cur="$1"; next="$2"; |
|
case "$cur" in |
|
-h|--help) Usage ; exit 0;; |
|
-v|--verbose) VERBOSITY=$((${VERBOSITY}+1));; |
|
-N|--no-defaults) defmaps=false;; |
|
-m) check_map "$next" || fail "bad map $next" |
|
case "$next" in |
|
u:*) umaps[${#umaps[@]}]=${next#*:};; |
|
g:*) gmaps[${#umaps[@]}]=${next#*:};; |
|
b:*) |
|
umaps[${#umaps[@]}]=${next#*:} |
|
gmaps[${#gmaps[@]}]=${next#*:} |
|
;; |
|
esac |
|
shift;; |
|
--) shift; break;; |
|
esac |
|
shift; |
|
done |
|
|
|
[ $# -ne 0 ] || { bad_Usage "must provide cmd"; return; } |
|
cmd=( "$@" ) |
|
|
|
TEMP_D=$(mktemp -d "${TMPDIR:-/tmp}/${0##*/}.XXXXXX") || |
|
fail "failed to make tempdir" |
|
trap cleanup EXIT |
|
|
|
if [ "$VERBOSITY" -gt 0 ]; then |
|
local uid="" gid="" name="" |
|
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" |
|
|
|
debug 1 "uid=$uid subuid=$subuid gid=$gid subgid=$subgid" |
|
fi |
|
|
|
FIFO="${TEMP_D}/fifo" |
|
mkfifo "$FIFO" || fail "failed mkfifo" |
|
exec 9<>$FIFO || fail "failed opening 9" |
|
|
|
_VERBOSITY=$((VERBOSITY-1)) unshare --user "$0" kid "${cmd[@]}" & |
|
KID=$! |
|
|
|
[ -d "/proc/$KID" ] || fail "$KID is dead" |
|
|
|
if [ "${defmaps}" = "true" ]; then |
|
[ "${#umaps[@]}" -eq 0 ] && umaps=( "0:$UID:1" ) |
|
[ "${#gmaps[@]}" -eq 0 ] && gmaps=( "0:$(id -g):1" ) |
|
fi |
|
|
|
remap newuidmap $KID "${umaps[@]}" || |
|
fail "failed to remap $KID with ${umaps[@]}" |
|
remap newgidmap $KID "${gmaps[@]}" || |
|
fail "failed to remap $KID with ${gmaps[@]}" |
|
|
|
echo 1 >&9 |
|
debug 2 "$$: waiting for $KID" |
|
wait $KID |
|
r=$? |
|
debug 2 "$$: done $r" |
|
return $r |
|
} |
|
|
|
if [ "$1" == "kid" ]; then |
|
shift |
|
kidmode "$@" |
|
exit |
|
fi |
|
main "$@" |