什么是非特权 LXC 容器?

0xC*_*22L 20 lxc userns

如果 Linux 容器(LXC 容器)被称为“非特权”是什么意思?

0xC*_*22L 20

非特权 LXC 容器是使用用户命名空间( ) 的容器。内核功能,允许的范围在主机上的UID映射到一个命名空间的即内部其中与UID 0,用户可以再次存在。

与我最初对非特权 LXC 容器的看法相反,这并不意味着容器必须由非特权主机用户拥有。那只是一种可能。

相关的是:

  1. 为主机用户定义了一系列从属 UID 和 GID ( usermod [-v|-w|--add-sub-uids|--add-sub-gids])
  2. ...并且此范围已映射到容器配置 ( lxc.id_map = ...)

所以甚至root可以拥有非特权容器,因为主机上容器进程的有效 UID 最终会在映射定义的范围内。

但是,root您必须先定义从属 ID。通过创建与用户不同adduserroot不会有一系列默认定义下属的ID。

还要记住,您提供的全部范围都由您支配,因此您可以拥有 3 个具有以下配置行的容器(仅显示 UID 映射):

  1. lxc.id_map = u 0 100000 100000
  2. lxc.id_map = u 0 200000 100000
  3. lxc.id_map = u 0 300000 100000

注意:根据评论,最近的版本称之为lxc.idmap

假设它root拥有 100000 到 400000 之间的从属 UID。我发现的所有文档都建议每个容器使用 65536 个从属 ID,不过有些使用 100000 使其更易于人类阅读。

换句话说:您不必为每个容器分配相同的范围。

拥有超过 40 亿 (~ 2^32) 个可能的从属 ID,这意味着您在向主机用户处理从属范围时可以很慷慨。

由 root 拥有和运行的非特权容器

再把它擦进去。非特权 LXC 来宾不需要由主机上的非特权用户运行。

使用从属 UID/GID 映射配置您的容器,如下所示:

lxc.id_map = u 0 100000 100000
lxc.id_map = g 0 100000 100000
Run Code Online (Sandbox Code Playgroud)

root主机上的用户拥有给定的下级 ID 范围的地方,将允许您更好地限制客人。

但是,在这种情况下还有一个重要的额外优势(是的,我已经验证它有效):您可以在系统启动时自动启动容器。

通常在网上搜索有关 LXC 的信息时,您会被告知无法自动启动无特权的 LXC 来宾。但是,默认情况下这仅适用于那些不在容器的系统范围存储中的容器(通常类似于/var/lib/lxc)。如果它们是(这通常意味着它们是由 root 创建并由 root 启动),那就是一个完全不同的故事。

lxc.start.auto = 1
Run Code Online (Sandbox Code Playgroud)

一旦你把它放入你的容器配置中,就会很好地完成这项工作。

正确获取权限和配置

我自己对此有点挣扎,所以我在这里添加了一个部分。

除了包含的配置片段之外,lxc.include它通常以名称/usr/share/lxc/config/$distro.common.conf$distro发行版的名称)表示,您应该检查系统上是否还有一个/usr/share/lxc/config/$distro.userns.conf,并将其包含在内。例如:

lxc.include = /usr/share/lxc/config/ubuntu.common.conf
lxc.include = /usr/share/lxc/config/ubuntu.userns.conf
Run Code Online (Sandbox Code Playgroud)

此外添加从属 ID 映射:

lxc.id_map = u 0 100000 65535
lxc.id_map = g 0 100000 65535
Run Code Online (Sandbox Code Playgroud)

这意味着主机UID 100000是root 内部的LXC客人的用户名称空间。

现在确保权限正确。如果您的客人的姓名将存储在环境变量中,$lxcguest您将运行以下命令:

# Directory for the container
chown root:root $(lxc-config lxc.lxcpath)/$lxcguest
chmod ug=rwX,o=rX $(lxc-config lxc.lxcpath)/$lxcguest
# Container config
chown root:root $(lxc-config lxc.lxcpath)/$lxcguest/config
chmod u=rw,go=r $(lxc-config lxc.lxcpath)/$lxcguest/config
# Container rootfs
chown 100000:100000 $(lxc-config lxc.lxcpath)/$lxcguest/rootfs
chmod u=rwX,go=rX $(lxc-config lxc.lxcpath)/$lxcguest/rootfs
Run Code Online (Sandbox Code Playgroud)

这应该允许您在第一次尝试可能出现一些与权限相关的错误后运行容器。

  • 很好的答案 - 但 `lxc` 不是这种事情的必需品。您可以使用 `util-linux` 工具 `unshare` 创建任何类型的命名空间容器。你可以使用 `util-linux` 工具 `nsenter` 进入所述容器。后一个工具还允许您从没有它的情况下将正在运行的进程添加到已经创建的容器中。命名空间支持在内核中实现。 (4认同)
  • @mikeserv:你的意思是你不需要 LXC 来使用 [tag:userns]?我知道。我也知道 Docker 现在有自己的库利用这些工具。但是,如果没有 LXC 提供的设施的帮助,您如何将整个系统容器化?你为什么要这样做?我的意思是包含一个应用程序并结合 `chroot` 这会有所帮助,但 LXC 结合了各种名称空间(UTS、mount 等)来容器化整个系统。 (4认同)
  • 嗯...正如我所说的,`unshare` 已经为任何/所有不同的命名空间做了令人钦佩的事情 - 甚至可以为您提供一个单独的、私有的 `/proc` 挂载,并带有单个 cli 开关。如果您的*单个应用程序* 是`init`,而您的`chroot` 是`initramfs`,那么您可以在几秒钟内获得一个完整的容器。 (2认同)