プロセス固有のインストールを作成するためにunshareを使用していますが、うまくいきます。
unshare -m --map-root-user
しかし、バインドマウントを作成した後
mount --bind src dst
whoami
UIDを元のユーザーに変更して、UID(および他のユーザー)が自分のユーザー名のようにエコーされるようにしたいと思いますecho $USER
。
私が試した答え unshareを使用したchrootシミュレーション
ただし、su – user1
これを実行したchroot /
後
su: Authentication failure
(Ignored)
setgid: Invalid argument
私はこれをUbuntu 18.04 Beta、Debian Stretch、openSUSE-Leap-42.3でテストしました。すべて同じです。この回答が機能してからカーネルで何かが変わったようです。
これを行う効率的で正しい方法は何ですか(もちろん本物根)?
答え1
これunshare(1)
このコマンドは以下を実行できません。
-r, --map-root-user
[...] 純粋に便利な機能で、複数のUIDやGIDの範囲マッピングなど、より複雑なユースケースをサポートしていません。
とにかく、補足グループ(video
、...)が失われる(またはマッピングされますnogroup
)。
2番目の新しいユーザー名前空間に戻ってマッピングを復元できます。unshare(1)
これを行わないので、カスタムプログラムが必要です。以下は、概念を証明するための非常にミニマルなCプログラムです(1人のユーザーのみ:uid / gid 1000/1000、エラーチェックなし)。私たちはそれを次のように呼びますrevertuid.c
。
#define _GNU_SOURCE
#include <sched.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int fd;
unshare(CLONE_NEWUSER);
fd=open("/proc/self/setgroups",O_WRONLY);
write(fd,"deny",4);
close(fd);
fd=open("/proc/self/uid_map",O_WRONLY);
write(fd,"1000 0 1",8);
close(fd);
fd=open("/proc/self/gid_map",O_WRONLY);
write(fd,"1000 0 1",8);
close(fd);
execvp(argv[1],argv+1);
}
これはマッピングdoneの逆マッピングに過ぎずunshare -r -m
、次のようにルートとuseになるためには避けられませんmount
。
$ strace unshare -r -m /bin/sleep 1 2>&1 |sed -n '/^unshare/,/^execve/p'
unshare(CLONE_NEWNS|CLONE_NEWUSER) = 0
open("/proc/self/setgroups", O_WRONLY) = 3
write(3, "deny", 4) = 4
close(3) = 0
open("/proc/self/uid_map", O_WRONLY) = 3
write(3, "0 1000 1", 8) = 8
close(3) = 0
open("/proc/self/gid_map", O_WRONLY) = 3
write(3, "0 1000 1", 8) = 8
close(3) = 0
execve("/bin/sleep", ["/bin/sleep", "1"], [/* 18 vars */]) = 0
だからこれは以下を提供します:
user@stretch-amd64:~$ gcc -o revertuid revertuid.c
user@stretch-amd64:~$ mkdir -p /tmp/src /tmp/dst
user@stretch-amd64:~$ touch /tmp/src/file
user@stretch-amd64:~$ ls /tmp/dst
user@stretch-amd64:~$ id
uid=1000(user) gid=1000(user) groups=1000(user)
user@stretch-amd64:~$ unshare -r -m
root@stretch-amd64:~# mount --bind /tmp/src /tmp/dst
root@stretch-amd64:~# ls /tmp/dst
file
root@stretch-amd64:~# exec ./revertuid bash
user@stretch-amd64:~$ ls /tmp/dst
file
user@stretch-amd64:~$ id
uid=1000(user) gid=1000(user) groups=1000(user)
またはより短く:
user@stretch-amd64:~$ unshare -r -m sh -c 'mount --bind /tmp/src /tmp/dst; exec ./revertuid bash'
user@stretch-amd64:~$ ls /tmp/dst
file
カーネル3.19以降、次のように動作が変更されることがあります。user_namespaces(7)
:
これ/proc/[pid]/setgroupsこのファイルはLinux 3.19に追加されましたが、セキュリティ上の問題を解決したため、以前の多くの安定したカーネルシリーズにバックポートされました。問題は、「rwx---rwx」などの権限を持つファイルに関連しています。
答え2
注:bwrapは現在util-linux> 2.39.1よりも多くの場所で使用できるため、この答えはbwrapを使用しますUbuntu 23.10以前のシステムでは、考慮する価値があります。 (一方、このソリューションは^ Cを正しく伝播しますが、bwrapは伝播しません)
ただし、bwrapとは異なり、このソリューションはunshareに基づいており、対話型CLI呼び出しに対して^ Cを正しく伝播します。
できない変化UIDですが、最新のutil-linuxを使用すると、root以外のユーザーとして起動し、それを使用してrootnsenter
権限を取得してマウントできます。
unshareを使用して名前空間を起動し、準備が完了するのを待ってから、偽のルートでいくつかのマウントを実行し、マウントが準備されたら、名前空間でタスクを実行する必要があります。
これには、マウントを実行する前に名前空間が準備されるのを待つのと、プロセスを実行する前にマウントが準備されるのを待つ2つの同期が必要です。
使用:
in-unshare [bind-mount] ... [--] command [arg] ...
例えば
in-unshare /build-dir=$PWD/build -- make build
# **Note:** this avoids an implicit exec with: `"$@" ; exit $?` which is necessary for proper signal propagation on ^C
# Who would have thought an extra waiting process layer would be so useful?
in-unshare() (
local session session_pid mounts
while test $# != 0
do case "$1" in
*=*) mounts+=("$1") ; shift ;;
--) shift ; break ;;
*) break;
esac
done
coproc mounter {
# session_pid will be the same as PPID but we need to wait until namespaces are setup
read -r session_pid || return $?
exec 0<&-
# mount as "root" via nsenter
for mount in "${mounts[@]}"
do # quit on error without writing to stdout
nsenter -U -m --target $session_pid mount "${mount#*=}" "${mount%=*}" -o rbind || return $?
done
# signal that mounts are setup
echo $?
}
exec {out}>>/dev/fd/${mounter[1]} {in}</dev/fd/${mounter[0]}
# note avoiding implict exec with "$@" ; exit $? is neccessary for proper signal shutdown on ^C
# who would have thought an extra waiting process layer would be so useful
exec unshare --mount --user --map-user=$(id -u) --map-group=$(id -g) --map-users=auto --map-groups=auto --keep-caps --setgroups allow /bin/bash --noprofile --norc -c "echo \$\$ >&${out} && exec ${out}>&- || exit \$? ; read -u ${in} && exec ${in}<&- && \"\$@\" ; exit $?" unshare "$@"
exit $?
)