非特权容器(Unprivileged Container) 权限映射问题及解决方法

在Proxmox VE (PVE) 中直接使用 LXC 运行应用(或者直接将 OCI/Docker 镜像转为 LXC 运行,而不套娃安装 Docker进程),是非常轻量且高效的做法。但这种做法最常遇到的就是无特权容器(Unprivileged Container)的权限与挂载问题

为了安全,PVE 默认创建的是无特权 LXC。它的核心机制是UID/GID 映射偏移(User Namespace)

  • LXC 容器内的 root (UID 0) = PVE 宿主机的 100000
  • LXC 容器内的普通用户 (如 UID 1000) = PVE 宿主机的 101000

当你在 PVE 宿主机上把一个硬盘目录通过 Bind Mount(挂载点)映射给 LXC 时,LXC 内部的 比如Syncthing 会因为宿主机目录的属主是 PVE 的 root (0),而容器内的 root 实际上是 100000从而导致没有读写权限(Permission Denied)

UID 映射断层场景分析

第一种情况

root@N3150:~# ls -lh /mnt/sdb/Download/
-rw-rw-r-- 1 root   root   1.8G Mar  5 08:29 proxmox-ve_9.1-1.iso
  • 在宿主机看来:文件属于 root (UID 0)。
  • 在容器内部看来: 在标准的 PVE 非特权容器中,如果一个文件在宿主机上属于真正的 root (UID 0),在容器内部 ls -l 看它,所有者通常不会显示为 root,而是会显示为 nobody(或者 nogroup / 65534)。因为容器内的 root (0) 已经被映射成了 100000,所以宿主机的真实 root (0) 对容器来说属于“映射范围之外的未知用户”,系统统统会将其显示为 nobody。

第二种情况

root@N3150:~# ls -lh /mnt/sdb/Download/
-rw-rw-r-- 1 root   root   1.8G Mar  5 08:29 proxmox-ve_9.1-1.iso
-rw-r--r-- 1 100000 100000 0 Mar 28 16:55 /mnt/sdb/Download/test
  • 在宿主机看来:文件属于 lxc容器用户 (UID 100000)。
  • 在容器内部看来: 在容器内部 ls -l 看它,所有者会显示为 root。

为什么 Download 能动,而 Compressed 动不了?

root@N3150:~# ls -lhd /mnt/sdb/Download
drwxrwxrwx 13 root root 4.0K Mar 28 16:55 /mnt/sdb/Download
root@N3150:~# ls -lhd /mnt/sdb/Download/Compressed/
drwxrwxr-x 9 root root 4.0K Mar 26 17:55 /mnt/sdb/Download/Compressed/

看目录权限位:

  • Download 目录:权限是 drwxrwxrwx (777) 或者你作为容器 root 有某种写入权。
  • Compressed 目录:权限是 drwxrwxr-x (775)。
    • 它的所有者是 root(宿主机的 root,ID 0)。
    • 它的组是 root(宿主机的组,ID 0)。
    • 重点: 容器里的 root(映射后是 100000)被这个目录视为 其他用户 (Others)。由于权限位最后一位是 5 (r-x),没有 w (写入) 权限,所以你进得去、看得见,但改不了。

解决方案

方案 1:在宿主机修改目录权限(最简单快速)

回到 PVE 的宿主机命令行(不是 LXC 容器里),直接把这个子目录的权限放开,让容器里的“伪 root”也能写:

# 在宿主机 N3150 上执行
chmod -R 777 /mnt/sdb/Download/Compressed/

优点:立刻生效。缺点:安全性较低,任何用户都能删文件。

方案 2:直接修改宿主机目录属主(适合该目录专属于容器)

如果这个宿主机目录就是专门为了给该 LXC 容器用的,宿主机和其他服务不需要去读写它,这是最暴力的做法。 在宿主机上,将该目录的所有权交给容器里的 root 映射出来的 ID(通常是 100000),或者,确认进程的 UID(比如 101000)和 GID(通常同 UID 也就是 101000)。

步骤:

  1. 确认 比如Syncthing 的实际运行 UID: 在 PVE 宿主机上,先让 LXC 里的 Syncthing 跑起来,然后在 PVE 终端运行:

    ps -auxn | grep syncthing
    

    看最左侧的 UID 是多少。通常有两种情况:

    • 如果容器内以 root 运行,宿主机看到的 UID 是 100000
    • 如果容器内以普通用户(如 1000)运行,宿主机看到的 UID 是 101000
  2. 在 PVE 宿主机上直接执行:

# 在宿主机 N3150 上执行,R后面的数值根据第一步的结果来确定
chown -R 100000:100000 /mnt/sdb/Download/Compressed/
  1. 在 PVE 的 GUI 界面或者用 pct 命令将目录挂载给 LXC。LXC 内部就会看到这个目录的拥有者是它自己而非nobody

方案 3:使用 PVE 的 UID 映射,LXC 配置文件自定义 UID/GID 映射(高级玩家)

如果你坚决要求 LXC 内的用户 UID(例如 1000)和 PVE 宿主机的 UID(1000)完全等同对应(穿透映射),你需要修改 PVE 的映射规则。这个方法配置稍微复杂,但在深度整合各种微服务时最彻底。

步骤:

  1. 修改 PVE 宿主机的 subuid 和 subgid 允许文件: 在 PVE 宿主机上编辑:

    nano /etc/subuid
    # 添加一行,允许root用户映射宿主机的UID 1000,数量为1个
    root:1000:1
    
    nano /etc/subgid
    # 同样添加
    root:1000:1
    
  2. 修改 LXC 容器的配置文件: 假设容器 ID 是 101,编辑 /etc/pve/lxc/101.conf,在文件末尾加上完整的映射逻辑:

    # 把容器内的 0-999 映射到宿主机的 100000-100999
    lxc.idmap: u 0 100000 1000
    lxc.idmap: g 0 100000 1000
    
    # 把容器内的 1000 精准映射到宿主机的 1000
    lxc.idmap: u 1000 1000 1
    lxc.idmap: g 1000 1000 1
    
    # 把容器内的 1001-65535 映射到宿主机的 101001-165535
    lxc.idmap: u 1001 101001 64535
    lxc.idmap: g 1001 101001 64535
    
  3. 重启 LXC 容器。此时,你在容器内用 UID 1000 写入的文件,在宿主机上查看,属主也是真实的 UID 1000(而不是 101000 了)。

方案 4:使用 ACL 精准放行权限(⭐ 最推荐,简单且优雅)

如果你希望宿主机的目录既能给比如 Syncthing 用,又不破坏宿主机原本的权限结构(比如你在宿主机上还跑了 Samba 共享),使用 Linux ACL(访问控制列表)是最完美的。

步骤:

  1. 确认 Syncthing 的实际运行 UID: 在 PVE 宿主机上,先让 LXC 里的 Syncthing 跑起来,然后在 PVE 终端运行:

    ps -auxn | grep syncthing
    

    看最左侧的 UID 是多少。通常有两种情况:

    • 如果容器内以 root 运行,宿主机看到的 UID 是 100000
    • 如果容器内以普通用户(如 1000)运行,宿主机看到的 UID 是 101000
  2. 在 PVE 宿主机上赋予 ACL 权限: 假设你要挂载给 Syncthing 的 PVE 目录是 /mnt/data/sync,Syncthing 的实际 UID 是 101000

    apt install acl -y  # 确保PVE安装了acl工具
    #
    # 赋予该UID读写执行权限
    setfacl -R -m u:101000:rwx /mnt/data/sync
    
    # 设置默认权限,确保未来新创建的文件也自动继承这个权限
    setfacl -R -d -m u:101000:rwx /mnt/data/sync
    

⚠️ 注意:上面两条命令必须都执行,第一条是让目标目录对于指定用户有rwx权限,第二条是确保继任者有rwx权限 3. 给 LXC 添加挂载点:

# <vmid> 是容器的ID,比如 101
pct set 101 -mp0 /mnt/data/sync,mp=/data

现在,容器内的 Syncthing 就可以畅通无阻地读写 /data 目录了。


解决方法一chmod -R 777 /mnt/sdb/Download/的隐患

执行 chmod -R 777 /mnt/sdb/Download/ 后,该目录下所有的子文件夹和文件的权限都会变得和他完全一样

1. -R 参数的含义

在 Linux 中,-R 代表 Recursive(递归)

  • 它会从你指定的目录开始,向下钻取到每一层子目录。
  • 无论是 CompressedMusic 还是深层嵌套的文件夹,都会被强制改为 rwxrwxrwx (777)。

2. 执行后的权限变化对照表

目标执行前 (以 Compressed 为例)执行后 (777)容器内 root 的权限
文件夹drwxrwxr-x (755)drwxrwxrwx (777)可以进入、创建、删除
文件-rw-rw-r-- (664)-rwxrwxrwx (777)可以修改、重命名、运行

3. 谁来执行?

虽然你执行了 chmod,但有一个细节需要注意:你在哪里执行的?

  • 如果在宿主机 (N3150) 执行: 这是最有效的。宿主机的 root 拥有最高权限,可以强行修改 sdb 挂载点下的所有属性。执行完后,LXC 容器里的 nobody(或者是映射后的 100000)因为属于 “Others”,现在拥有了 7 (rwx) 权限,问题立即解决。

  • 如果在容器 (LXC) 内部执行: 如果你在容器里看到 Compressed 的所有者是 nobody 且你没有写入权,你可能无法对该目录执行 chmod。因为你不是该目录的所有者。


4. 更加“精细”的建议

如果你不想让所有文件都变成“可执行”状态(777 会让普通文档在 Linux 下也变成绿色可执行文件),你可以只针对目录开启权限:

宿主机执行:

# 只把所有目录设为 777,方便进入和创建
find /mnt/sdb/Download/ -type d -exec chmod 777 {} +

# 把所有文件设为 666,方便读写但不赋予执行权
find /mnt/sdb/Download/ -type f -exec chmod 666 {} +

解决方法二chown -R 100000:100000 /mnt/sdb/Download/的局限

由于:谁创建的文件,谁就是所有者。

“在哪里” 以及 “用什么方式” 添加文件会产生截然不同的结果。


场景一:你在 PVE 宿主机命令行手动添加

如果你直接在 PVE 宿主机的终端(N3150)里执行 cptouch

  • 结果: 文件权限是 root:root (UID 0)。
  • 在容器里看: 这个文件会再次显示为 nobody 或者无法修改,因为它的 UID 是 0,而容器里的 root(UID 100000)对它来说只是个“陌生人”。
  • 解决方法: 每次在宿主机手动放文件进去后,都需要再跑一次 chown 100000:100000

场景二:你在 LXC 容器内部添加

如果你进入了容器,在容器的 shell 里执行 touch newfile

  • 结果: 容器认为所有者是 root
  • 在宿主机(PVE)看: 这个文件的所有者会自动显示为 100000:100000
  • 原因: PVE 的内核会自动处理非特权容器的映射关系。

场景三:通过 Aria2/Samba 等服务自动添加

  • 如果是容器内的 Aria2 下载的: 文件所有者就是 100000,没问题。
  • 如果是宿主机的服务往里写的: 文件所有者就是该服务的运行用户(通常是 root 或宿主机的普通用户)。

如何让 PVE 宿主机添加文件时自动变成 100000?

如果你经常要在宿主机操作这些文件,又懒得每次改权限,有几个“偷懒”的办法:

1. 强制继承所有者 (SetGID 位)

你可以给 Download 目录设置一个特殊的标志,让在该目录下创建的新文件自动继承父目录的“组”:

# 在宿主机执行
chown -R 100000:100000 /mnt/sdb/Download/
chmod -R g+s /mnt/sdb/Download/

注:这能解决“组”的问题,但“所有者”通常还是创建者本人。

2. 最省心的办法:在宿主机用容器身份创建

如果你非要在宿主机命令行操作,可以伪装成容器用户:

# 在宿主机执行,以 UID 100000 的身份创建文件
sudo -u #100000 touch /mnt/sdb/Download/new_file.txt

这样创建出来的文件,容器里看就是完美的 root 权限。

3. 终极方案:ACL (访问控制列表)

你可以设置一个默认的权限规则,不管谁创建文件,都给 UID 100000 完整的读写权:

# 在宿主机执行
apt install acl # 如果没安装的话
setfacl -R -d -m u:100000:rwx /mnt/sdb/Download/
setfacl -R -m u:100000:rwx /mnt/sdb/Download/

设置好 ACL 后,以后不管宿主机 root 丢什么文件进去,容器里的 root 都能像亲儿子一样直接修改它。


解决方法三UID Mapping

上面介绍的时候介绍了将 普通用户 id=1000 映射到lxc容器,此处介绍root id=0 修改 LXC 配置文件是一种**“一劳永逸”**的方案,它的核心逻辑是:告诉 PVE,把宿主机的 root (0) 和容器里的 root (0) 看作同一个人。

这种操作被称为 UID 映射(UID Mapping)


1. 修改配置的原理

默认情况下,非特权容器的映射关系是:

  • 容器 UID 0 $\rightarrow$ 宿主机 UID 100000
  • 宿主机 UID 0 $\rightarrow$ 容器 nobody (因为超出了映射范围)

通过修改配置,我们可以插入一条特殊规则:让容器的 UID 0 直接对应宿主机的 UID 0(或者对应的某个特定 UID)。

2. 操作步骤

风险提示: 这样做会严重降低容器的隔离安全性,但在个人 NAS 或实验环境下非常常用。

第一步:修改容器配置文件

PVE 宿主机上,编辑该容器的配置文件(假设你的容器 ID 是 101):

nano /etc/pve/lxc/101.conf

在文件末尾添加以下四行(这代表将 UID/GID 从 0 开始的 1 个用户直接映射):

lxc.idmap: u 0 0 1
lxc.idmap: g 0 0 1
lxc.idmap: u 1 100001 65534
lxc.idmap: g 1 100001 65534
  • 前两行: 把容器的 UID/GID 0 映射到宿主机的 UID/GID 0。
  • 后两行: 把剩下的 65534 个 ID 映射到正常的 100001 范围,保证其他系统用户正常。

第二步:修改宿主机权限允许映射

你需要告诉系统,root 用户允许被映射进容器。修改以下两个文件:

  1. nano /etc/subuid,添加一行:root:0:1
  2. nano /etc/subgid,添加一行:root:0:1

第三步:重启容器

pct shutdown 101
pct start 101

3. 修改后的效果对照

操作方式修改前 (默认)修改后 (配置映射)
宿主机 root 丢入文件容器内显示 nobody,无法写容器内显示 root可以直接写
容器 root 创建文件宿主机显示 100000宿主机显示 root
安全性极高(完全隔离)略低(容器 root 等同于宿主机 root)

4. 这种方案的优缺点

  • 优点:
    • 双向无缝: 不管你在宿主机还是容器里操作,文件永远属于 root,永远有权限。
    • 无需手动 chown: 再也不用担心下载工具或手动拷贝导致权限丢失。
  • 缺点:
    • 安全性隐患: 如果容器被黑,黑客拿到容器 root 权限,就直接等同于拿到了宿主机的 root 权限。
    • 配置略复杂: 如果你有多个用户(比如还有普通用户 1000),映射规则会变得很长。

解决方法四ACL进阶设置

场景分析

  • 高频跨容器文件共享(强权限关联) 包含:247 (Aria2Pro) 、 254 (alpine-samba) 、 246 (Syncthing) 这几个服务通常需要共享宿主机的同一个硬盘目录(比如:Aria2 下载文件到 /data/downloads,Samba 把这个目录共享到局域网,Syncthing 再把它同步走)。

  • 面临问题: Aria2Pro 容器内的 UID 可能是 1000(宿主机 101000),alpine-samba 容器内的运行 UID 可能是 0(宿主机 100000)。如果不做处理,Aria2 下载的文件,Samba 会提示无权读取,Syncthing 也无权修改。

  • 最合理设置:使用 ACL 实现“多重赋权” 这是多容器共享同一目录的唯一终极优雅方案。不要用 chown 改来改去,直接在 PVE 宿主机上给这个共享目录叠加权限:

  1. 确认三个容器的主进程 UID(假设 Aria2 是 101000,Samba 是 100000,Syncthing 是 101000)。

  2. 在 PVE 宿主机执行:

    # 假设你挂载给它们的物理目录都是 /mnt/storage/data
    
    # 给 Aria2 和 Syncthing (UID 101000) 读写执行权限
    setfacl -R -m u:101000:rwx /mnt/storage/data
    setfacl -R -d -m u:101000:rwx /mnt/storage/data
    
    # 给 Samba (UID 100000) 读写执行权限
    setfacl -R -m u:100000:rwx /mnt/storage/data
    setfacl -R -d -m u:100000:rwx /mnt/storage/data
    
  3. 如何确认结果 检查方法

这样,这几个无特权 LXC 挂载同一个目录时,互相产生的文件都可以自由读写,绝不会出现权限冲突。

原理解释

在传统的 Linux 系统中,一个文件确实只能有一个真正的“属主(Owner)”和一个“属组(Group)”。

但是,setfacl 命令使用的并不是传统的 Linux 归属权(chown),而是 ACL(Access Control List,访问控制列表)

可以用 **“房产证”**和 “备用钥匙” 来打个比方:

1. 传统权限(chown / chmod)—— 房产证上写谁的名字

传统的 Linux 权限就像是房产证。 一栋房子(一个文件),房产证上只能写一个人的名字(属主 Owner)。 如果你用 chown 100000 /mnt/storage/data,就相当于把这栋房子过户给了 Samba (100000)。 这时候,如果 Aria2 (101000) 想进这栋房子放东西,保安一看房产证上不是你的名字,就会把它拒之门外(Permission Denied)。如果你又把房子过户给 Aria2,Samba 就又进不去了。

2. ACL 权限(setfacl)—— 发放“备用钥匙”

ACL 的机制是在不改变“房产证”的情况下,给其他人配发“备用钥匙”。 一个文件依然只有一个真正的属主,但是它可以有一张“白名单(ACL)”,上面写着:“除了房主之外,某某某也可以进来”。

我们回头看这两行命令:

# 给 UID 101000 配发一把拥有 读、写、执行 (rwx) 权限的钥匙
setfacl -R -m u:101000:rwx /mnt/storage/data

# 给 UID 100000 也配发一把拥有 读、写、执行 (rwx) 权限的钥匙
setfacl -R -m u:100000:rwx /mnt/storage/data

执行完之后,这个文件夹的实际属主可能依然是 PVE 宿主机的 root(UID 0),但是:

  • Samba (100000) 拿着备用钥匙,可以自由读写。
  • Aria2 和 Syncthing (101000) 也拿着备用钥匙,同样可以自由读写。

这就是为什么在多容器共享同一个目录时,ACL唯一完美的解决方案


进阶:那个 -d 是干嘛用的?(关于新创建的文件)

你会注意到代码里有带 -d 的命令:

setfacl -R -d -m u:100000:rwx /mnt/storage/data

这里的 -d 代表 Default(默认继承)

想象一个场景:Aria2 下载完了一个新电影(新文件)。在 Linux 规则里,谁创建了文件,谁就是这个新文件的“房主”。 如果没有 -d,这个新电影的房产证上写的只有 Aria2 (101000),Samba (100000) 依然打不开这个新电影。

但是有了 -d 之后,相当于给这个大文件夹立下了一个**“祖训”**: “在这个目录下诞生的任何新文件,在出生的那一刻起,必须自动给 101000 和 100000 这两个人配发备用钥匙!”

这样一来,不管是谁在这个文件夹里创建了新文件,另外几个人都能立刻对其进行读取、修改或者删除操作,彻底解决了不同 LXC 容器之间共享文件时的权限扯皮问题。

怎么查看这个“钥匙名单”?

如果你想看看这个目录到底给了哪些人钥匙,你可以在 PVE 宿主机上使用 getfacl 命令:

getfacl /mnt/storage/data

你会看到类似这样的输出:

# file: mnt/sdb/Download/Compressed/
# owner: root
# group: root
user::rwx  <-- 宿主机 root 的权限
user:100000:rwx <-- 就是这一行!它代表“当前”这个目录,(100000) 已经拥有读、写、执行的最高权限了!是执行第一条setfacl命令的结果
group::rwx <-- 宿主机 group 的权限
mask::rwx
other::r-x <-- 其他人的权限 (只有读 r 和执行 x,没有写 w)
default:user::rwx <-- 【注意看这里往下全是 default】是第二条setfacl命令执行结果
default:user:100000:rwx  <-- 成功设置了 default (默认继承)
default:group::rwx
default:mask::rwx
default:other::r-x

ACL 的“后悔药”(如何撤销 ACL)

如果你不小心写错了 UID,或者以后不想共享这个文件夹了,可以使用以下命令撤销:

  • 删除特定用户的权限(收回某人的钥匙): setfacl -R -x u:101000 /mnt/storage/data setfacl -R -d -x u:101000 /mnt/storage/data
  • 清空该目录下所有的 ACL 规则(恢复传统的 Linux 权限): setfacl -R -b /mnt/storage/data

ACL 到底存储在哪里?(底层原理)

ACL 设置并没有像 /etc/passwd101.conf 那样集中存储在某个统一的配置文件里。

这也就意味着,无法通过打开某一个单一的“系统记录本”来查看全盘的 ACL 设置。

为了透彻理解,我们继续用“房产证”来打比方,并教如何高效找出系统中所有的 ACL 规则。

传统的权限(chown/chmod)和 ACL 权限(setfacl),它们都是直接记录在硬盘文件系统的 “元数据(Inode / xattr)” 里的。

在 Linux 文件系统(如 ext4, xfs, zfs)中,有一种机制叫 扩展属性(Extended Attributes,简称 xattr)

  • 如果说传统权限是直接印在“房产证”正面的字。
  • 那么 ACL 就像是用强力胶水死死粘在房产证背面的一张“便签纸”

结论: ACL 属性是跟着文件本身走的。文件在哪里,ACL 记录就在哪里。它被打散、深深刻录在了你那块硬盘的底层数据结构中。

⚠️ 衍生出的一个重要常识(备份与复制)

正因为 ACL 是粘在文件上的扩展属性,当用普通的 cp 命令或者普通的 tar 压缩时,这张“便签纸”可能会掉!

  • 复制文件时保留 ACL:需要用 cp -a(archive 归档模式)。
  • rsync 同步时保留 ACL:需要加 -A 参数(rsync -aA)。
  • tar 压缩时保留 ACL:需要加 --acls 参数。

怎么查看 PVE 里“所有”的 ACL 设置?

既然没有统一的配置文件,要看所有的 ACL 设置,就只能**“全盘扫描”**去找那些贴了便签纸的文件。

系统提供了一个非常强大的专属排查参数:-s (skip base)。 普通文件都有基础权限(就是传统的 rwx),加上 -s 之后,系统就会忽略所有普通文件,只把那些拥有“特殊 ACL 备用钥匙”的文件揪出来。

方法一:扫描特定硬盘或目录(⭐ 强烈推荐)

假设你的数据盘挂载在 /mnt/sdb,你可以执行:

# -R 代表递归排查,-s 代表只显示拥有 ACL 的文件
getfacl -R -s /mnt/sdb/

执行后,屏幕上只会输出那些你单独设置过 ACL 的目录和文件,结果非常清晰:

# file: mnt/sdb/Download
# owner: root
# group: root
user::rwx
user:100000:rwx
user:101000:rwx
...

(⚠️ 警告:尽量不要直接扫描 PVE 的根目录 /,比如 getfacl -R -s /,因为这会扫描整个系统盘,包括 /proc/sys 等虚拟内存目录,不仅非常慢,还会报出一堆权限错误。针对你的数据盘目录扫描即可。)

方法二:日常最直观的肉眼分辨法(+ 号标记)

如果你平时在终端里闲逛,想快速知道面前的这些文件谁被设置了 ACL,直接用最常用的列出文件命令:

ls -lh /mnt/sdb/Download/

仔细看权限列表的最右侧。正常的权限是以点 . 结尾的,而被施加了 ACL 魔法的文件,末尾会变成一个加号 +

# 正常文件,没有 ACL
drwxr-xr-x.  2 root root 4.0K Mar 29 10:00 Normal_Folder

# 末尾带有 + 号!说明这个文件有隐藏的 ACL 备用钥匙!
drwxrwxrwx+ 13 root root 4.0K Mar 28 16:55 Compressed

一旦你看到哪个文件夹带了 + 号,你就可以针对它运行 getfacl 去看看里面到底藏着什么白名单了。


高级技巧:如何批量备份和恢复 ACL?

既然 ACL 是分散在各个文件上的,万一重装了 PVE 系统,或者把硬盘格式化了再把文件倒腾回来,ACL 钥匙丢了怎么办?

可以用一条命令把某个目录下所有的 ACL 规则抽离出来,保存成一个纯文本文件(相当于打造一本“钥匙花名册”)

备份当前目录下所有 ACL 到文本:

getfacl -R /mnt/sdb/Download > /root/acl_backup.txt

你可以用 cat /root/acl_backup.txt 看看,里面记录了每个文件夹的绝对路径和它对应的钥匙白名单。

一键恢复所有 ACL: 如果哪天权限乱了,或者迁移了系统,只需在新系统里执行:

setfacl --restore=/root/acl_backup.txt

系统就会根据这本“花名册”,瞬间把所有的备用钥匙重新配发、粘贴到对应的文件上去!


网络代理与底层网络设备权限设置(设备直通需求)

比如v2rayasing-box这类服务是网络代理层,它们最大的特点是:需要在容器内创建虚拟网卡(TUN/TAP 设备)来进行流量接管或 VPN 路由。 默认的无特权 LXC 出于安全考虑,是死活无法创建 TUN 设备的,会报错 /dev/net/tun: No such file or directoryOperation not permitted最合理设置:给这两个 LXC 开启 TUN 设备映射

需要修改 PVE 宿主机上这两个容器的配置文件。

  1. PVE 终端编辑配置(以 252 v2raya 为例):

    nano /etc/pve/lxc/252.conf
    
  2. 在文件末尾添加以下两行(这适用于 PVE 8.x/9.x 的 Cgroup V2 环境):

    lxc.cgroup2.devices.allow: c 10:200 rwm
    lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
    
  3. 保存并重启 252253 容器。 注:这类网络服务不涉及复杂的宿主机文件读写(最多就是读写自己的 config 目录),并且为了备份完整方便,一般不挂载目录


补充说明

常见的网络存储挂载大坑(NFS/SMB)

无特权 LXC 还有一个很大的特征是直接拒绝挂载网络文件系统

⚠️ 额外避坑:关于挂载局域网的 SMB/NFS 共享 如果你的电影、文件在群晖等另一台 NAS 上,千万不要试图直接在无特权 LXC 内部执行 mount -t cifs/nfs,这会直接报错 Operation not permitted(因为无特权容器被 AppArmor 和内核隔离了底层的网络文件系统挂载权限)。 正确做法是: 先在 PVE 宿主机的系统里(Datacenter -> Storage 或者直接改 /etc/fstab)把 SMB/NFS 挂载到宿主机,然后再通过本文讲的 Bind Mount(挂载点) 映射给 LXC,最后再配合本文的 ACL 方案 解决权限问题。

Docker 嵌套(给非原生应用党)

**“在无特权 LXC 里跑 Docker”**遇到了权限问题。他们的底层逻辑和这里介绍的是完全一样的。

对于在 LXC 里跑 Docker 的用户 如果你没有像我一样彻底解耦,而是依然在 LXC 里安装了 Docker,本文的权限方案(尤其是 ACL)完全通用。只要注意在 PVE 创建 LXC 时,在“选项 (Options)”里勾选 嵌套 (Nesting)Keyctl,Docker 就能在无特权 LXC 里完美运行并拥有合理的目录权限。

总结建议

  1. 坚持无特权(Unprivileged): 尽量不要为了偷懒把上面任何一个容器改成“特权容器(Privileged)”,目前的 PVE 机制下,结合 ACL (针对文件)lxc.cgroup2 (针对设备),无特权容器能完美解决所有问题。
  2. 如果你的 Download 文件夹里只是电影、安装包等非敏感数据修改配置文件是最舒服的,因为操作体验和虚拟机一样。
  3. 如果你非常看重 PVE 宿主机的安全性,建议维持现状,使用我上一条提到的 ACL (setfacl) 方案,它只针对特定目录放开权限,不改动系统级的 UID 映射。