JuNest
一个轻量级的基于 Arch Linux 的发行版,可以在任何其他 Linux 发行版上运行,无需 root 权限。
目录
描述
JuNest(Jailed User Nest)是一个轻量级的基于 Arch Linux 的发行版,它允许在任何通用的 GNU/Linux 宿主操作系统内创建一次性的、部分隔离的 GNU/Linux 环境,而无需 root 权限即可安装软件包。
JuNest 围绕 Arch Linux 的包管理器 pacman 构建,可以访问 Arch Linux 仓库中的大量软件包。
使用 JuNest 的主要优势包括:
- 无需 root 权限即可安装软件包。
- 创建部分隔离的环境,可以安装软件包而不会影响生产系统。
- 访问更广泛的软件包,特别是在存储库相对有限的 GNU/Linux 发行版(如 CentOS 和 Red Hat)上。
- 通过 QEMU 在与宿主操作系统不同的架构上运行。
- 可用于
x86_64
和arm
架构,但你也可以从头开始构建自己的镜像! - 所有 Arch Linux 爱好者都可以在任何地方享受他们喜爱的发行版!
JuNest 遵循 Arch Linux 哲学。
JuNest 与 Docker 和 Vagrant 有何不同?
尽管 JuNest 听起来类似于虚拟化/Linux 容器系统,但 JuNest 与 Docker 或 Vagrant 等解决方案有很大不同。事实上,JuNest 的目的不是构建一个完全隔离的环境,而是相反,提供能够运行程序的能力,就像它们是从宿主操作系统本地运行一样。宿主操作系统和 JuNest 沙箱之间几乎所有内容都是共享的(内核、进程子树、网络、挂载等),只有根文件系统被隔离(因为安装在 JuNest 中的程序需要驻留在其他地方)。
这允许宿主操作系统和 JuNest 之间的进程进行交互。例如,你可以在 JuNest 中安装 top
命令,并使用它来监控属于宿主操作系统的进程。
安装
依赖项
JuNest 只有很短的依赖项列表,以便能在大多数 GNU/Linux 发行版中安装。 在安装 JuNest 之前,请确保所有依赖项都已正确安装在你的系统中:
从 git 仓库安装
只需将 JuNest 仓库克隆到某个位置(例如 ~/.local/share/junest):
git clone https://github.com/fsquillace/junest.git ~/.local/share/junest
export PATH=~/.local/share/junest/bin:$PATH
你可以选择使用包装器来直接从宿主系统运行安装在 JuNest 中的命令:
export PATH="$PATH:~/.junest/usr/bin_wrappers"
更新你的 ~/.bashrc
或 ~/.zshrc
以始终使包装器可用。
使用 AUR 安装(仅限 Arch Linux)
如果你使用的是 Arch Linux 系统,你也可以从 AUR 仓库 安装 JuNest。
JuNest 将位于 /opt/junest/
快速入门
设置环境
首先需要通过 JUNEST_HOME
环境变量(必须包含绝对路径)在您选择的位置安装 JuNest 环境,默认位置是 ~/.junest
:
junest setup
该脚本将从仓库下载镜像并将其放置在默认目录 ~/.junest
中。
访问环境
JuNest 使用 Linux 命名空间(即 ns
)作为默认后端程序。要通过 ns
访问,只需输入:
junest
您可以使用 sudo
命令获取 fakeroot 权限并安装/删除软件包。
或者,您可以使用 -f
(或 --fakeroot
)选项来获取 fakeroot 权限,而无需一直使用 sudo
:
junest -f
另一种执行模式是通过 Proot:
junest proot [-f]
有多种后端程序,每种都有其优缺点。要了解更多关于 JuNest 根据使用的后端程序的执行模式,请参阅下面的使用部分。
直接从主机操作系统运行 JuNest 安装的程序
在 JuNest 中安装的程序可以直接从主机访问,无需进入 JuNest 会话(即无需先调用 junest
命令)。例如,假设主机操作系统是 Ubuntu 发行版,您可以通过简单更新 PATH
变量直接运行 pacman
:
export PATH="$PATH:~/.junest/usr/bin_wrappers"
sudoj pacman -S htop
htop
默认情况下,包装器使用 ns
模式。要使用 ns --fakeroot
,您可以使用方便的命令助手 sudoj
。
要更多地控制后端模式,您也可以使用 JUNEST_ARGS
环境变量。
例如,如果您想以真实 root 权限运行 iftop
:
sudoj pacman -S iftop
sudo JUNEST_ARGS="groot" iftop
可以随时重新创建二进制包装器(例如,如果由于某些原因它们被损坏):
junest create-bin-wrappers -f
每次在 JuNest 内安装可执行文件时,都会自动生成二进制包装器。
这仅适用于位于 /usr/bin
路径中的可执行文件。
对于其他位置的可执行文件(比如 /usr/mybinpath
),您只能通过执行以下命令手动创建包装器:
junest create-bin-wrappers --bin-path /usr/mybinpath
显然,要访问相应的二进制包装器,您需要相应地更新 PATH
变量:
export PATH="$PATH:~/.junest/usr/mybinpath_wrappers"
从 AUR 安装软件包
在 ns
模式下,您可以使用已经可用的 yay
命令轻松地从 AUR 仓库安装软件包。在 proot
模式下,JuNest 不再支持构建 AUR 软件包。
请记住,为了从 AUR 构建软件包,首先需要 base-devel
软件包组:
pacman -S base-devel
JuNest 使用由 junest/sudo-fake
提供的修改版 sudo
。在安装 base-devel
时,原始的 core/sudo
软件包将被忽略**(且不得安装)**。
玩得开心!
如果您是 Arch Linux 新手,不熟悉 pacman
软件包管理器,请访问 pacman rosetta 页面。
使用方法
根据您决定使用的后端程序,有三种不同的方式运行 JuNest。
基于 Linux 命名空间
Linux 命名空间是 JuNest 的默认后端程序。 使用 Linux 命名空间的要求是:
- 从 Linux 3.8 开始的内核允许非特权进程创建用户和挂载命名空间。
- Linux 内核发行版必须启用用户命名空间。
近年来,大多数 GNU/Linux 发行版默认启用了用户命名空间。这意味着您不需要 root 权限就可以通过这种方法访问 JuNest 环境。 这个维基提供了几个 GNU/Linux 发行版上用户命名空间的状态。
要通过 Linux 命名空间运行 JuNest:
- 作为普通用户 - 允许进行基本操作或使用
sudo
命令安装/删除软件包:junest ns
或junest
- 作为 fakeroot - 允许安装/删除软件包:
junest ns -f
或junest -f
这种模式基于出色的 bubblewrap
命令。
基于 PRoot
Proot 是一种便携的解决方案,允许非特权用户在沙盒内执行程序,适用于大多数可用的 GNU/Linux 发行版。
要通过 Proot 运行 JuNest:
-
作为普通用户 - 允许进行基本操作:
junest proot
-
作为 fakeroot - 允许安装/删除软件包:
junest proot -f
在 proot
模式下,主机操作系统的最低推荐 Linux 内核版本是 x86(64 位)和 ARM 架构上的 2.6.32。在较低的 2.6.x 主机操作系统内核上仍然可以运行 JuNest,但可能会出现错误,一些应用程序可能会崩溃。有关更多信息,请阅读下面的故障排除部分。
基于 Chroot
此解决方案仅适用于特权用户。JuNest 提供了通过 chroot
程序运行环境的可能性。
具体来说,它使用了一个名为 GRoot
的特殊程序,这是 arch-chroot 包装器的小型便携版本,允许在 JuNest 沙箱内执行任何程序之前绑定挂载用户指定的目录,如 /proc
、/sys
、/dev
、/tmp
、/run/user/<id>
和 $HOME
。如果挂载不起作用,JuNest 甚至提供了直接通过纯 chroot
命令运行环境的可能性。
要通过 chroot
解决方案运行 JuNest:
-
以 root 身份通过
GRoot
- 允许在 JuNest 环境中拥有完全的 root 权限(执行此操作需要 root 权限):junest groot
-
以 root 身份通过
chroot
- 允许在 JuNest 环境中拥有完全的 root 权限(执行此操作需要 root 权限):junest root
执行模式比较表
下表显示了每个后端程序能够执行的功能:
QEMU | 需要 Root 权限 | 管理官方包 | 管理 AUR 包 | 可移植性 | 支持 | 用户模式 | |
---|---|---|---|---|---|---|---|
Linux 命名空间 | 否 | 否 | 是 | 是 | 差 | 是 | 普通用户和 fakeroot |
Proot | 是 | 否 | 是 | 否 | 是 | 是 | 普通用户和 fakeroot |
Chroot | 否 | 是 | 是 | 是 | 是 | 是 | 仅 root |
高级用法
构建镜像
你可以通过运行以下命令从头开始构建新的 JuNest 镜像:
junest build [-n]
该脚本将创建一个包含所有必要文件的目录,以使 JuNest 正常工作(如 pacman
和 proot
)。
选项 -n
将跳过最终的验证测试(如果不需要的话)。
请记住,构建镜像的脚本必须在安装了 arch-install-scripts 和 base-devel 软件包的 Arch Linux 操作系统中运行。
要更改构建目录,只需使用 JUNEST_TEMPDIR
(默认为 /tmp)。
创建镜像 junest-x86_64.tar.gz
后,你可以通过运行以下命令安装它:
junest setup -i junest-x86_64.tar.gz
如需更多详细信息,你还可以查看 junest-builder,其中包含用于自动构建 JuNest 镜像的脚本和 systemd 服务。
相关 wiki 页面:
通过 QEMU 使用不同架构运行 JuNest
以下命令将下载 ARM JuNest 镜像,并在主机操作系统运行在 x86_64
架构的情况下运行 QEMU:
$> export JUNEST_HOME=~/.junest-arm
$> junest setup -a arm
$> junest proot -- uname -m
armv7l
绑定目录
要将主机目录绑定到客户端位置:
junest -b "--bind /home/user/mydata /mnt/mydata"
或使用 proot 参数:
junest proot -b "-b /mnt/mydata:/home/user/mydata"
选项 -b
用于向后端程序提供选项,适用于 PRoot、Namespace 和 GRoot 后端程序。
通过传递 --help
选项检查后端程序选项:
junest [u|g|p] -b "--help"
Systemd 集成
尽管 JuNest 并非设计为完整的容器,但通过 systemd 容器 仍然可以虚拟化进程树。JuNest 容器允许在容器内运行服务,这些服务可以通过网络从主机操作系统看到。 这样做的缺点是主机操作系统必须使用 systemd 作为服务管理器,并且容器只能使用 root 权限执行。
要启动 JuNest 容器:
sudo systemd-nspawn -bD ~/.junest
相关 wiki 页面:
内部原理
所有依赖主机操作系统可执行文件的自动回退
JuNest 首先尝试运行位于不同位置的主机操作系统中的可执行文件(/usr/bin
、/bin
、/usr/sbin
和 /sbin
)。
作为回退,如果 JuNest 环境中有相同的可执行文件,它会尝试运行该可执行文件。
JuNest 镜像的自动构建
仅针对 x86_64
架构定期自动构建 JuNest 镜像。
arm
架构的 JuNest 镜像可能并非总是最新的,因为构建是手动执行的。
静态 QEMU 二进制文件
JuNest 镜像中包含静态 QEMU 二进制文件,允许在与主机系统不同的架构上运行 JuNest。它们位于 /opt/qemu
目录中。
故障排除
有关 Arch Linux 相关的常见问题解答,请查看通用故障排除页面。
无法使用 AUR 仓库
问:为什么我在尝试安装软件包时会收到以下错误? 找不到压缩 man 和 info 页面所需的 gzip 二进制文件。
答: JuNest 自带的软件包数量非常有限。 为了安装 AUR 软件包,你需要先安装
base-devel
软件包组, 其中包含了所有从源代码编译所需的基本软件包(如 gcc、make、patch 等):
#> pacman -S base-devel
请记住不要安装
core/sudo
,因为它与junest/sudo-fake
软件包冲突。
无法将用户和组设置为 root
问: 在 ns 模式下安装软件包时,我遇到了以下错误:
警告:提取 /usr/file... 时出现警告(无法为 /usr/file... 设置用户=0/组=0)
答: 这是因为在 fakeroot 环境中无法将文件的所有者/组设置为 root。尽管显示了这条消息,软件包仍会正确安装。
无法在 pacman 中更改根目录
未为存储库配置服务器
问: 为什么我无法安装软件包?
#> pacman -S lsof
软件包 (1): lsof-4.88-2
总下载大小: 0.09 MiB
总安装大小: 0.21 MiB
错误:未为存储库配置服务器:core
错误:未为存储库配置服务器:community
错误:无法提交事务(未为存储库配置服务器)
发生错误,没有软件包被升级。
答: 你只需要根据你的位置更新镜像列表文件:
# 根据你的位置取消注释相应的存储库行
#> nano /etc/pacman.d/mirrorlist
#> pacman -Syy
定位特定文件所属的软件包
问: 如何查找某个文件属于哪个软件包?
答: JuNest 是一个非常小的发行版,因此你经常需要找出某个文件所属的软件包名。
pkgfile
是一个非常有用的软件包,可以帮你检测给定文件所属的软件包。 例如,如果你想找出getopt
命令所属的软件包名:
#> pacman -S pkgfile
#> pkgfile --update
$> pkgfile getop
core/util-linux
或者,你也可以直接使用
pacman
命令。详情请查看这里。
内核太旧
问: 为什么我会收到错误:"致命错误:内核太旧"?
答: 这是因为预编译软件包中的二进制文件是为 Linux 内核 2.6.32 编译的。当不带其他选项启动 JuNest 时,它会尝试从 JuNest chroot 环境运行一个 shell。系统发现宿主操作系统的内核太旧,因此拒绝启动 shell。
解决方法是向 JuNest chroot 环境提供一个更高的"伪造"内核版本。PRoot 提供了 -k 选项来实现这一点,当使用 -p 前缀时,JuNest 会将这个选项传递给 PRoot。例如,要伪造一个 3.10 版本的内核,请使用以下命令:
$> junest proot -b "-k 3.10"
由于 Arch Linux 为内核版本 2.6.32 提供二进制文件,上述错误不仅出现在 JuNest 的预编译软件包中。当尝试运行后来使用
pacman
命令在 JuNest chroot 环境中安装的二进制文件时,也会出现这个错误。
要检查 JuNest chroot 环境中的可执行文件是否与宿主操作系统的内核兼容,只需使用
file
命令,例如:
$> file ~/.junest/usr/bin/bash
ELF 64-位 LSB 可执行文件,x86-64,版本 1 (SYSV),动态链接
(使用共享库),适用于 GNU/Linux 2.6.32,
BuildID[sha1]=ec37e49e7188ff4030052783e61b859113e18ca6,已剥离
输出显示了推荐的最低 Linux 内核版本。
内核不支持私有 futex
问: 为什么我会收到警告:"kompat:此内核不支持私有 futex,PRoot 无法模拟它们。"?
答: 当在较旧的宿主操作系统内核上应用向 JuNest chroot 环境显示伪造内核版本的技巧时(参见上文:内核太旧),就会发生这种情况。
向 JuNest chroot 环境显示伪造内核版本的后果是,PRoot 需要在后台将 chroot 环境中应用程序的请求转换为宿主操作系统的旧内核。一些较新的内核功能可以被模拟,但私有 futex 无法被转换。
私有 Futex 在 Linux 内核 2.6.22 中引入。因此,上述问题很可能出现在旧的 Linux 系统上,例如基于 Linux 内核 2.6.18 的 RHEL5 系统。许多核心工具如
which
、man
或vim
可以正常运行,而其他工具,尤其是基于 XOrg 的程序,更可能显示警告。这些程序也更可能出现意外崩溃。
目前,没有(简单的)解决方法。为了完全兼容 2.6.22 以下的内核,JuNest 的预编译软件包和之后安装的所有软件都需要针对该内核进行编译。这很可能只能通过从源代码构建所需的软件包来实现,这与 JuNest 的发行版中的发行版理念有些矛盾。
SUID 权限
问: 为什么我没有 ping 的权限?
$> ping www.google.com
ping: icmp open socket: 操作不被允许
答: ping 命令使用 suid 权限,允许以 root 权限执行命令。fakeroot 模式无法执行设置了 suid 的命令,你可能需要使用 root 权限。还有一些其他命令也有 suid 权限,你可以用以下命令列出 JuNest 环境中的这些命令:
$> find /usr/bin -perm /4000
图形应用程序中没有可见字符
问: 为什么我安装的应用程序中看不到任何字符?
答: 这可能是因为系统中没有安装字体。
要快速解决这个问题,你可以安装一个字体包:
#> pacman -S gnu-free-fonts
文件系统和包所有权的差异
问: 为什么使用 root 权限安装包时会收到警告?
#> pacman -S systat
...
warning: 目录所有权在 /usr/ 上不同
文件系统: 1000:100 包: 0:0
...
答: 这种情况下包的安装仍然顺利完成了。每次使用 root 权限安装包时都会出现这种情况,因为 JuNest 会尝试通过将文件所有权分配给真实用户来保留 JuNest 环境。
内核编译时禁用了非特权用户命名空间或内核版本太旧
问: 为什么通过 Linux 命名空间运行 JuNest 时会收到这个警告?
$> junest ns
非特权用户命名空间在内核编译时被禁用或内核版本太旧(<3.8)。无论如何继续执行...
答: 这意味着 JuNest 检测到宿主操作系统要么没有较新的内核版本,要么在内核编译时未启用非特权用户命名空间。JuNest 不会停止程序的执行,而是尝试继续运行。如果无法调用命名空间,请尝试使用 Proot 作为后端程序。
非特权用户命名空间被禁用
问: 为什么通过 Linux 命名空间运行 JuNest 时会收到这个警告?
$> junest ns
非特权用户命名空间被禁用。需要 root 权限才能启用它: sudo sysctl kernel.unprivileged_userns_clone=1
答: 这意味着 JuNest 检测到宿主操作系统要么没有较新的 Linux 版本,要么用户命名空间未启用。JuNest 不会停止程序的执行,而是尝试继续运行。如果你有 root 权限,请尝试启用它,否则请尝试使用 Proot 作为后端程序。
更多文档
JuNest wiki 页面中有更多教程。
贡献
欢迎贡献!你可以通过以下方式帮助改进 JuNest:
- [报告 Bug](https://github.com/fsquillace/junest/blob/master/CONTRIBUTING.md#reporting-bugs
- [提出改进建议](https://github.com/fsquillace/junest/blob/master/CONTRIBUTING.md#suggesting-enhancements
- [编写代码](https://github.com/fsquillace/junest/blob/master/CONTRIBUTING.md#your-first-code-contribution
捐赠
为了维持项目,请考虑通过GitHub 赞助页面进行捐赠。
作者
JuNest 最初由 Filippo Squillace (feel.sqoox@gmail.com) 于 2014 年底创建。
这里列出了非常感谢的贡献者!