unshare --map-root-user는 설정 후 원래 uid/사용자 이름으로 전환합니다.

unshare --map-root-user는 설정 후 원래 uid/사용자 이름으로 전환합니다.

프로세스별 설치를 생성하기 위해 unshare를 사용하고 있는데 잘 작동합니다.

unshare -m --map-root-user

그러나 바인드 마운트를 만든 후

mount --bind src dst

whoamiUID를 원래 사용자로 변경하여 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)됩니다.

두 번째 새 사용자 네임스페이스로 다시 변경하여 매핑을 복원할 수 있습니다. unshare(1)이 작업을 수행하지 않으므로 사용자 정의 프로그램이 필요합니다 . 다음은 개념 증명을 위한 매우 미니멀한 C 프로그램입니다(한 명의 사용자만: 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를 사용하면 루트가 아닌 사용자로 시작한 다음 이를 사용하여 nsenter루트 권한을 얻고 마운트할 수 있습니다.

unshare를 사용하여 네임스페이스를 시작하고 준비될 때까지 기다린 다음 가짜 루트로 일부 마운트를 수행하고 마운트가 준비되면 네임스페이스에서 작업을 실행하도록 해야 합니다.

이를 위해서는 마운트를 수행하기 전에 네임스페이스가 준비될 때까지 기다리는 것과 프로세스를 실행하기 전에 마운트가 준비될 때까지 기다리는 두 가지 동기화가 필요합니다.

사용:

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 $?
)

관련 정보