我正在尝试为最小的源代码 linux 发行版编写引导程序。
我想在一个类似 chroot 的环境中构建。这应该简化包装。在这一点上,我不关心安全性。引导程序不应需要任何非标准的第三方命令。如果不需要 root 那就太好了。
这就是为什么 fakechroot(1) fakeroot(1) chroot(1) 不是我正在寻找的。
是否可以伪造 / 使用 unshare(1) 和 /bin/sh?
我正在使用 unshare 来创建每个进程的挂载,它运行得非常好
unshare -m --map-root-user
Run Code Online (Sandbox Code Playgroud)
但是,在创建了我的绑定安装之后
mount --bind src dst
Run Code Online (Sandbox Code Playgroud)
我想将 UID 更改为我的原始用户,以便whoami(和其他人)像echo $USER这样响应我的用户名。
我已经尝试过Simulate chroot with unshare的答案
但是,在做su – user1之后chroot /,我得到
su: Authentication failure
(Ignored)
setgid: Invalid argument
Run Code Online (Sandbox Code Playgroud)
我已经在 Ubuntu 18.04 Beta、Debian stretch、openSUSE-Leap-42.3 上测试过这个。全部都是一样。我猜自从这个答案起作用以来,内核中发生了一些变化。
什么是可行且正确的方法来做到这一点(当然没有真正的根)?
我观察到以下我无法解释的现象。添加CAP_SYS_ADMIN功能后,unshare不再能够写入/proc/self/setgroups.
实际上,写入此文件需要该功能,但这是通过更改用户命名空间来实现的。那么为什么向父进程添加功能会阻止写入此文件呢?
me@myhost:~$ unshare -r
root@myhost:~# exit
logout
me@myhost:~$ sudo setcap cap_sys_admin=ep /usr/bin/unshare
me@myhost:~$ unshare -r
unshare: cannot open /proc/self/setgroups: Permission denied
me@myhost:~$ sudo setcap cap_sys_admin= /usr/bin/unshare
me@myhost:~$ unshare -r
root@myhost:~#
Run Code Online (Sandbox Code Playgroud)
顺便说一句:我正在运行内核版本为 4.4 的 Ubuntu 16.04.4 LTS,并且 util-linux(包括unshare)的版本为 2.27.1。
我正在使用unshare执行诸如将本地绑定安装到某个进程而不需要 root 访问权限之类的操作,例如:
unshare -mr bash mount --bind a b
Run Code Online (Sandbox Code Playgroud)
(是的,这看起来有点愚蠢;在我的实际用例中;unshare正在运行一个执行绑定安装的 bash 脚本。我没有在这里这样做,所以它是一个较小的例子。)
但是,如果我尝试循环安装,它会失败:
ryan@DevPC-LX ~/stuff/util-linux master $ unshare -mr mount -o loop x.img a
mount: no permission to look at /dev/loop<N>
Run Code Online (Sandbox Code Playgroud)
:/
我尝试使用mknod创建一个假循环设备(需要非 root 用户无法使用的权限)、手动运行losetup(仍然需要 root 权限)以及一堆其他不起作用的东西。
当然,我可以这样做chown myuser /dev/loop*,但这似乎可能成为一个主要的安全问题。
此外,guestmount对于我的用例来说太慢了,并且fuseext2有关于写入模式下可能丢失数据的警告(而且它也太慢了)。
有没有办法做到……这个?根本?
从这个答案中我们了解到,您可以通过 Linux PID 命名空间实现对整个进程子树的可靠终止unshare -p。
这是我不明白的问题:
仅当我使用-f/--fork选项取消共享时它才有效。
unshare -fp -- bash -c "watch /bin/sleep 10000 && echo hi"
Run Code Online (Sandbox Code Playgroud)
当我运行这个时,kill -9那个的PID bash,然后观看,睡眠等都死了,正如我所希望的那样。
但是当我使用它时没有-f:
unshare -p -- bash -c "watch /bin/sleep 10000 && echo hi"
Run Code Online (Sandbox Code Playgroud)
和kill -9bash PID,然后将其watch重新设置为PID 1(在我的Ubuntu上是systemd),所以我没有达到杀死所有孩子的预期效果。
问题:
--fork必须要达到预期的效果?为什么不unshare使用exec()fork 还不够?kill -9而创建的 PID 。unshare但是当我使用 时--fork,杀死我开始时返回的 pidunshare将简单地杀死unshare并bash …我正在使用第三方 .NET Core 应用程序(VS Code 扩展使用的二进制发行版),不幸的是,该应用程序启用了诊断日志记录,但没有明显的方法来禁用它(我已经向作者报告了这一点)。理想的解决方案(除了能够禁用它之外)是,如果我可以指定 systemd 它不应该为该特定程序记录任何内容,但我一直无法找到任何方法来执行此操作。这是迄今为止我尝试过的所有内容:
我尝试的第一件事是重定向stdout并stderr到/dev/null: dotnet-app > /dev/null 2>&1。这确实禁用了任何正常输出,但诊断日志记录仍在写入 systemd 日志。
我希望应用程序有一个命令行参数,允许我禁用诊断日志记录。它确实有一个冗长的参数,但经过实验,它似乎只对正常输出有影响,对诊断日志记录没有影响。
通过使用strace并查找对 的调用connect,我发现应用程序将诊断日志直接写入到/dev/log.
该路径/dev/log是 的符号链接/run/systemd/journal/dev-log,因此为了验证我的发现,我将符号链接更改为指向/dev/null。这确实阻止了诊断日志记录出现在 systemd 日志中。
有人告诉我LD_PRELOAD并制作了一个库,connect用我自己的版本替换了标准,在尝试连接到/dev/log. 这在我的测试程序中工作正常,但在 .NET Core 应用程序中失败,在connect ENOENT /tmp/CoreFxPipe_1ddf2df2725f40a68990c92cb4d1ff1e. 我尝试了我的库,但即使我所做的只是直接将参数传递给标准connect函数,它仍然会失败并出现相同的错误。
然后,我尝试使用 Linux 命名空间来实现它,以便仅/dev/log指向/dev/null.NET Core 应用程序:unshare --map-root-user --mount sh -c "mount --bind /dev/null /dev/log; dotnet-app $@". …
我想我可以做这样的事情:
\nsudo unshare -T bash -c \'date -s "$1" && foobar\' sh "$(date -d -1day)"\nRun Code Online (Sandbox Code Playgroud)\n所以foobar会看到与系统其他部分不同的系统时间。但是,似乎不包含系统时间的变化。它改变了整个系统的系统时间。
这篇 LWN 文章似乎表明这个命名空间是为了我试图赋予它的用途而设计的。
\n\n\n当在根时间命名空间之外调用时,调整系统时间的系统调用将调整特定于命名空间的偏移量。
\n
看着strace date -s ...,我看到了其他输出:
clock_settime(CLOCK_REALTIME, {tv_sec=1619044910, tv_nsec=0}) = 0\nRun Code Online (Sandbox Code Playgroud)\n然而,阅读time_namespaces(7):
\n\n这会影响针对这些时钟进行测量的各种 API,包括:clock_gettime(2)、clock_nanosleep(2)、nanosleep(2)、timer_settime(2)、timerfd_settime(2) 和 /proc/uptime。
\n
我看到它没有提到clock_settime(2)。“包括”一词告诉我这可能不是完整的列表,但也许是。
我也不懂--boottime/ --monotonic。看着clock_settime(2),我看到:
\n\nCLOCK_MONOTONIC 不可设置的系统范围时钟,表示自 \xe2\x80\x94 以来的单调时间,如 POSIX\xe2\x80\x94 所描述的“过去的某个未指定点”。在 Linux 上,该点对应于系统自启动以来运行的秒数。
\n
\nCLOCK_BOOTTIME(自 Linux …
是否有命令可以检查容器服务是否在 Linux 系统上运行?有人建议,unshare但我不确定这是否是最好的方法。
我观察到以下几点:
作为 1 号 shell 中的非特权用户:
user@box:~$ sysctl kernel.unprivileged_userns_clone
kernel.unprivileged_userns_clone = 1
user@box:~$ unshare --mount --user
nobody@box:~$ echo $$
18655
Run Code Online (Sandbox Code Playgroud)
并作为 2 号 shell 中的 root:
root@box:~# mkdir -p /tmp/myns
root@box:~# touch /tmp/myns/{user,mnt}
root@box:~# mount --bind /proc/18655/ns/user /tmp/myns/user
root@box:~# mount --bind /proc/18655/ns/mnt /tmp/myns/mnt
mount: /tmp/myns/mnt: wrong fs type, bad option, bad superblock on /proc/18655/ns/mnt, missing codepage or helper program, or other error.
Run Code Online (Sandbox Code Playgroud)
该错误令人惊讶:我无法将挂载命名空间绑定挂载到文件,但我可以将用户命名空间绑定挂载到文件?为什么会这样,我怎样才能使这个挂载命名空间可供非特权用户使用user?
为什么我想要这个:为了测试一个程序,我想~user用一个临时文件系统覆盖,最初共享原始内容。可以按照以下方式由 root 设置
tmp='/tmp/GAtcNNeSfM8b'
mkdir -p "$tmp"
mount -t tmpfs -o size=100m …Run Code Online (Sandbox Code Playgroud) 我有一个在网络命名空间中运行的应用程序。这效果很好。
我想在不同的命名空间中多次运行该应用程序。为了方便起见,我想将应用程序的工作目录绑定到命名空间内的 /tmp/nsX 之类的目录。
如果我只是mount --bind /tmp/nsX /var/lib/my-app在命名空间中执行此操作,那么当我退出命名空间时,挂载就会消失。
通过进入/退出命名空间,我的意思是ip netns exec bash
我正在看unshare,nsenter但我不知道该怎么做。
我想要:
如果我需要使用其他一些命名空间类型,那也没有问题。
使用 时unshare --pid --fork,该nsenter命令必须附加到子 pid 而不是unsharepid 以获取正确的 pid 命名空间。
我可以按如下方式获取 unshare 的 pid:
unshare --pid --mount --fork --mount-proc bash &
echo PID: $!
fg
Run Code Online (Sandbox Code Playgroud)
但我需要unshare孩子的 pid (2914003) 才能输入正确的命名空间:
ps wwfuax | grep -A1 unshare
2914002 pts/4 S 0:00 | \_ unshare --pid --mount --fork --mount-proc bash
2914003 pts/4 S+ 0:00 | \_ bash
Run Code Online (Sandbox Code Playgroud)
这有效:nsenter -t 2914003
这不起作用:nsenter -t 2914002
我希望有某种选择,unshare --show-child-pid但没有。
获取取消共享子进程 pid 的可靠可靠方法是什么?
该手册页指定你可能会感兴趣的使用--fork和--mount-proc创建PID命名空间的时候,偏偏这些选项不是默认?
unshare ×12
namespace ×10
linux ×5
process ×2
bind-mount ×1
cgroups ×1
chroot ×1
clock ×1
containers ×1
gdb ×1
kill ×1
ld ×1
loop-device ×1
mount ×1
root ×1
unix-sockets ×1
users ×1