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)。

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

関連情報