最近帮一位同事排查串口设备权限问题,他把自己的账号加进了dialout组,id命令也确认了,但手里的串口工具仍然报“Permission denied”。这种“组加了却不生效”的情况,我在麒麟桌面系统V10-SP1 2503里见过不止一次。用户组是Linux权限模型里最基础的一环,但恰恰因为基础,很多人容易忽略它背后的机制。这篇文章从麒麟桌面的常见用户组入手,把每个组是干什么的、什么时候需要加、加完不生效怎么排查讲清楚。无论你是管理员还是普通桌面用户,收藏这份内容应该都能少踩几次坑。
1. 用户组在麒麟桌面权限体系里的真实角色
1.1 文件权限三位一体的检查逻辑
Linux系统的权限判断,本质上不是看“你是谁”,而是看“你的身份编号”和“你所在的组编号列表”。任何文件、设备节点,权限位都被拆成三段:属主、属组、其他用户。日常用ls -l看到的形如-rw-rw----的输出,第二位到第四位是属主权限,第五位到第七位是属组权限,最后三位是其他用户权限。
当某个进程尝试访问文件时,内核先看进程的有效用户ID是否等于文件的属主UID,相等就套用属主权限;如果不相等,再看进程的组ID列表里有没有哪个组ID等于文件的属组GID,有则套用属组权限;都没有才落到第三段“其他用户”权限上。
这个“组ID列表”很关键。Linux允许一个用户同时属于多个组,这和其他一些操作系统只能属于一个主组的设计完全不同。麒麟桌面V10-SP1 2503沿用了这套标准POSIX权限模型,所以你从其他Linux发行版带过来的经验在这里基本都能用。区别只在于,桌面版为了照顾普通用户使用音频、插U盘、连接网络等场景,预置的组更多,默认状态也更宽松。
1.2 主组与附加组:为什么一个用户能同时属于这么多组
用户身份信息分散在三个文件里:
/etc/passwd记录用户名、UID、主组GID、登录Shell等信息;/etc/group记录组名、GID、组成员列表;/etc/gshadow记录组密码和组管理员(一般用不到)。
其中,/etc/passwd里第四个字段就是主组(primary group)。用户新建文件时,文件属组默认继承主组。而/etc/group的成员列表里出现的组,是附加组(supplementary groups)。一个用户可以只有一个主组,但附加组可以有几十个。
为什么这么设计?因为权限检查是“或”的关系:只要组ID列表里有任意一个组和文件的属组匹配,就按属组权限处理。所以系统管理员可以让一个用户既是sudo组成员,又是docker组成员,同时还在dialout里,三个组的权限同时生效,互不冲突。
1.3 三条命令快速看清自己的组身份
想看当前用户的所有身份,直接在终端执行:
id输出大概长这样:
uid=1001(zhangsan) gid=1001(zhangsan) groups=1001(zhangsan),4(adm),20(dialout),27(sudo),44(video),46(plugdev),108(lpadmin)这里gid=1001(zhangsan)是主组,后面的groups=列表里除主组外全是附加组。想看某一个具体用户,加用户名即可:id zhangsan。
只想看组名,可以用:
groups想确认某个组在系统里是否存在、有哪些成员,用:
getent group sudo输出如sudo:x:27:zhangsan,lisi,表示该组的GID是27,当前成员有zhangsan和lisi。这三个命令是最常用的,也是后面排查问题的第一步。
2. 高频用户组逐个拆解:每个组该不该加,加错了有什么后果
2.1 管理提权组:sudo与wheel
在麒麟桌面上,最常打交道的是sudo组。默认安装时,/etc/sudoers里通常已经配置了一行类似%sudo ALL=(ALL:ALL) ALL的规则,意思是sudo组的所有成员可以在任何主机上以任何用户身份执行任何命令。安装系统时创建的第一个管理员账号,默认就在这个组里。
另一个常见的提权组是wheel。这套叫法源自CentOS、openEuler这类发行版,麒麟桌面V10的不同内核路线版本里,有的默认支持sudo组,有的也会读取wheel组。判断标准很简单:打开/etc/sudoers或/etc/sudoers.d/下的文件,看看里面写的是%sudo还是%wheel,哪个才是真正生效的提权组。
这里有个很容易踩的坑:把用户加进了sudo组,但sudoers文件里可能根本没有配置%sudo这一行。在这个场景下,用户即使属于sudo组,执行sudo命令依然会提示“不在sudoers文件中”。所以加组之后,第一件事不是开个终端试sudo whoami,而是先确认规则存在。
2.2 硬件设备访问组:cdrom、audio、video、plugdev、dialout与input
桌面版和服务器版的最大区别,就是桌面上到处都是硬件设备节点。而这些节点的权限,大部分靠组来控制。
cdrom:光驱设备访问组。如今用得少了,但插入光驱或虚拟机映射光驱时,不在这个组里可能无法挂载。audio:声卡设备访问组。图形桌面安装时普通用户默认在组里,不在的话可能听不到声音。video:显卡渲染和视频加速设备访问组。硬件解码、GPU绘图都需要它。plugdev:麒麟V10桌面版最重要的组之一。U盘、移动硬盘、手机MTP模式等可插拔设备能否被普通用户挂载,主要看这个组。插上U盘没反应时,先别急着怪系统,查一下用户是否在plugdev组里。dialout:串口设备访问组。/dev/ttyUSB0、/dev/ttyACM0、/dev/ttyS0这类节点的属组基本都是dialout,开发板调试、工业设备通信、4G模块拨号都靠它。input:直接读取输入事件的组。游戏外设、按键映射工具会用到,成员理论上可以读取键盘原始输入事件,有潜在的记录风险,桌面普通用户默认不会加入。
这些组里,disk是最需要警惕的。/dev/sda这类整块磁盘设备的属组是disk,组成员可以直接读写整块硬盘,相当于拥有绕过文件权限的底层访问能力。普通用户绝不能随便加入。
2.3 日志、网络与系统观察组:adm、systemd-journal、netdev
系统日志也不全是要root才能看。adm组成员可以直接读取/var/log下的传统文本日志;systemd-journal组成员可以执行journalctl查看systemd日志。这两个组对排查问题很有用,给运维同事加上之后,他们不需要提权也能看启动日志和应用日志。
网络管理方面,麒麟桌面用的是NetworkManager,控制它的大部分操作现在走polkit权限,和用户组的关系不大。但某些点上还有一个netdev组,成员可以执行部分网络配置命令。如果用户反馈网卡无法连接Wi-Fi而别人可以,检查一下是否在这个组里。
2.4 存储与共享相关组:dip、sambashare、fuse、storage
这几个组容易被忽略,但实际遇到时很头疼。
dip来自拨号时代,现在主要和dialout一起出现,某些串口拨号程序仍然依赖它。sambashare是Samba服务相关的组,配置了Samba文件共享后,想让某些用户向共享目录写文件,通常需要把用户加入这个组,否则只能读不能写。fuse组成员允许挂载FUSE类型的文件系统,比如用户态挂载的加密目录、网盘目录。storage和plugdev是搭档,都影响可移动设备的识别。
遇到“U盘能识别但打不开”“共享目录能看见但写不进”这类问题,十有八九是组权限没配全,而不是网络或防火墙的问题。
3. 容易被忽略的组和机制:虚拟化、容器、抓包与免密提权
3.1 kvm、docker、wireshark:工具链专属组的权限边界
开发者和运维人员常用的工具,也会创建自己的组。
kvm组管理/dev/kvm设备节点。要用QEMU/KVM跑虚拟机并开启硬件加速,用户必须在kvm组里,否则虚拟机只能以纯软件模拟方式运行,速度慢到让人怀疑人生。如果打开虚拟机提示“KVM不可用”,先查查这个组。
docker组的情况则要谨慎看待。系统安装Docker后,守护进程监听一个Unix Socket,这个Socket的属组通常是docker。组成员可以通过Docker CLI直接和守护进程通信,而容器里的root权限和宿主机root高度相关,把一个普通用户加入docker组,基本等同于给了这个用户root级权限。除非你完全信任该用户,否则不要因为“方便”随手把人加进去。
wireshark组,或者部分版本里的wireshark抓包工具专用组,允许成员以普通用户身份执行抓包程序读取网络数据。抓包能力会接触到网络里的明文流量,在多人共享的办公系统里要慎重开放。
3.2 “加入sudo组就能免密?”——NOPASSWD机制的常见误解
很多人以为加入sudo组之后,sudo时就不用输密码了,这个理解是错的。默认配置下,sudo组只是“允许提权”,每次执行sudo仍然要输入当前用户密码。
真正的免密取决于/etc/sudoers或/etc/sudoers.d里的具体规则,比如:
zhangsan ALL=(ALL) NOPASSWD: ALL或者针对组:
%sudo ALL=(ALL) NOPASSWD: ALL这类配置要改的话,务必用sudo visudo来编辑,它会检查语法错误。手动改/etc/sudoers一旦写错,整个sudo就废了,救都救不回来。免密配置是有实际代价的:任何能接触到这个用户会话的人都能直接提权,所以生产环境里我基本不推荐给桌面用户开全局免密,顶多针对一两条命令做NOPASSWD。
3.3 图形自动登录与会话组:哪些“组”其实不是组
在桌面环境里还有一个容易混淆的点:自动登录、免密解锁、指纹登录这些功能,和用户组没有直接关系。它们由显示管理器(图形登录界面程序)和PAM模块配置控制,比如自动登录写在显示管理器的配置文件里,指纹认证写在/etc/pam.d下。所以当你听到“加入某个组就能让系统自动登录”这种说法时,基本可以判定是误解。
但另一方面,图形会话确实会和某些组产生联动。比如video组成员在登录时能访问DRM设备,组缺失可能导致桌面会话起不来或者花屏。也就是说,桌面相关的组更像是会话启动的“前置条件”,而不是登录方式的开关。
4. 用户组管理实操:命令、参数以及最容易翻车的覆盖问题
4.1 创建用户与把用户加入组的正确姿势
新建用户时直接指定附加组,一条命令就好:
sudo useradd -m -s /bin/bash -G sudo,plugdev,dialout zhangsan-m创建家目录,-s指定登录Shell,-G后跟逗号分隔的附加组。如果用户已经存在,再追加组,用:
sudo usermod -aG sudo,plugdev,dialout zhangsan-aG的意思是append到现有附加组列表后面。这个-a非常关键,少了它,-G会把用户原先所有的附加组全部替换掉,只留下你当前指定的这些组。
4.2 修改主组与附加组:usermod的几个参数最容易用错
usermod -g用来修改主组,注意是小写g:
sudo usermod -g developers zhangsan修改后,用户新建文件的属组默认就会变成developers。这个操作影响面大,一般不建议随意改。
usermod -G不带-a覆盖附加组列表的坑,值得单独讲一次。我见过不止一个管理员为了给用户加一个docker组,执行usermod -G docker user,结果用户瞬间从sudo、plugdev、dialout等所有附加组里被踢出来,连sudo都用不了。所以请记住一条铁律:日常加组只用-aG,只有刻意清空用户的全部附加组时才单独用-G。
如果只是临时切换当前shell的主组,不需要改系统配置,用newgrp:
newgrp dialout这条命令会启动一个新shell,把当前主组临时切换成指定组,退出后恢复。它适合临时测试某个组权限,不会对用户配置产生永久改动。
4.3 目录与文件的组归属、setgid继承机制
单独给用户加组只是第一步,文件系统层面的组权限同样要理清。修改文件或目录的属组:
sudo chgrp -R developers /srv/project给目录设置setgid位:
sudo chmod g+s /srv/project设置setgid位之后,目录下新建的文件会自动继承目录的属组,而不是创建者自己的主组。这是共享协作目录的经典做法:多个用户只要都在developers组里,创建的文件组归属就是一致的,配合组读写权限,团队协作就不用来回chgrp了。
4.4 组文件损坏与一致性检查:grpck、vipw / vigr
系统用户和组信息都落在/etc/passwd、/etc/group、/etc/shadow、/etc/gshadow四个文件里,它们必须保持一致。指令:
sudo grpck会检查组文件里引用的用户名、属组是否都能对应上。发现问题后它会给出互动式修复建议,一般选择删除无效条目即可。要改这些文件时,不要直接拿编辑器写,用安全工具:
sudo vipw sudo vigr它们在打开文件之前会加锁,避免多个管理员同时编辑导致互相覆盖。曾经有人在组文件里手滑删了一个逗号,结果几十个用户同时丢失附加组权限,那场面相当狼狈。
5. 一个串口权限问题的完整排查过程:从“加了组”到“组没生效”
5.1 症状与第一步:设备文件权限到底谁写的
回到开头那个案例。同事使用的USB转串口设备,接入麒麟桌面后生成了/dev/ttyUSB0节点。报错信息是:
cannot open /dev/ttyUSB0: Permission denied这时候千万不能上来就sudo chmod 777 /dev/ttyUSB0,治标不治本,重启后节点重新生成,权限又回去了。先看设备节点本身:
ls -l /dev/ttyUSB0输出:
crw-rw---- 1 root dialout 188, 0 Jun 10 14:22 /dev/ttyUSB0设备属组是dialout,权限位是crw-rw----,属主root有读写权限,属组dialout有读写权限,其他用户没有任何权限。这就说明,普通用户想访问,必须把自己加入dialout组。
5.2 第二步:用户确实在dialout组里
同事说他已经加过组了,我看了一下:
id zhangsan输出里确实有:
uid=1001(zhangsan) gid=1001(zhangsan) groups=1001(zhangsan),20(dialout),27(sudo)用户在主数据库层面的身份没问题。那为什么还是Permission denied?
5.3 根因定位:进程的组列表在登录时固化
这里涉及一个很多人不清楚的机制:进程的组ID列表,是由登录会话初始化时固定的。登录成功后,shell以及后续启动的所有进程,都会继承这一份组列表的快照。之后就算管理员把用户加入了新组,已经存在的shell进程也不会感知到变化,它依然带着旧的组列表在运行。
验证方法很简单,在已经报错的终端里执行:
groups如果输出里没有dialout,就说明这个shell的组列表还是旧的。id命令读的是用户数据库,是“应该成为什么样”;而groups在当前shell里输出的是“当前进程实际拥有什么”。两个结果会出现短暂的不一致,这正是“加了组却不生效”的最常见原因。
5.4 修复方案与验证
修复办法有三个,按推荐顺序来:
- 彻底退出登录,重新登录一次,开新的终端会话。桌面环境就注销后重新登录,SSH就断开重连。这是最彻底、最省事的方案。
- 临时在当前shell里执行
newgrp dialout,只对当前shell生效,适合救急。 - 紧急情况下,临时用
sudo minicom -s跑一次,先把活儿干完,回头再重新登录。
重新登录后,再执行groups,看到dialout出现了,串口工具直接就能打开。同样的道理也适用于sudo组、docker组等一切附加组的变更。
另外还有两个衍生场景值得提醒。如果用了tmux这类终端复用器,旧的session同样会保留旧组列表,重开一个session或整体重启tmux才行。如果是桌面图形程序,比如文件管理器,那就得完全注销图形会话,只关终端窗口不解决任何问题。
6. 组管理速查表与安全底线:对照检查不出错
6.1 常用组速查表
| 组名 | 主要作用 | 涉及设备/场景 | 默认是否需要 | 风险说明 |
|---|---|---|---|---|
| sudo | sudo提权 | /etc/sudoers配置 | 管理员必须 | 提权能力,需谨慎 |
| wheel | 替代sudo的提权组 | 部分版本默认 | 视版本而定 | 同上 |
| adm | 读取/var/log日志 | 系统日志 | 运维建议加 | 低风险 |
| systemd-journal | 读取journalctl日志 | 系统日志 | 运维建议加 | 低风险 |
| cdrom | 光驱挂载 | /dev/cdrom | 一般不需要 | 低风险 |
| audio | 声卡访问 | /dev/snd/* | 桌面默认 | 可干扰录音 |
| video | 显卡/视频加速 | /dev/dri/* | 桌面默认 | 低风险 |
| plugdev | U盘/移动设备挂载 | 可移动介质 | 桌面默认 | 低风险 |
| dialout | 串口设备访问 | /dev/ttyUSB*、ttyACM* | 视需要 | 可操作串口设备 |
| dip | 拨号/部分串口程序 | 调制解调器 | 视需要 | 低风险 |
| input | 原始输入事件 | /dev/input/* | 不建议 | 有键盘记录风险 |
| disk | 整块磁盘访问 | /dev/sd* | 绝不建议 | 等同于root |
| kvm | 虚拟机硬件加速 | /dev/kvm | 虚拟化用户需要 | 仅限虚拟化 |
| docker | Docker socket访问 | 容器管理 | 谨慎添加 | 等同root |
| wireshark | 抓包权限 | 网络流量 | 谨慎添加 | 可读取网络明文 |
| sambashare | Samba共享写入 | 文件共享 | 共享用户需要 | 低风险 |
| fuse | 用户态文件系统挂载 | 加密盘/网盘 | 视需要 | 低风险 |
| netdev | 网络设备管理 | NetworkManager | 视需要 | 低风险 |
6.2 安全底线:不该做的几件事
基于这些年的经验,整理几条组管理的底线:
- 不要用
chmod 777代替组配置。设备节点、目录权限一旦放开,所有用户都能访问,相当于把权限模型的根基拆了。 - 不要把普通用户加进
disk组或docker组。前者可绕过文件权限直接读盘,后者可通过容器控制宿主机,两者都等同给root。 - 不要用
-G覆盖附加组。清空一组权限的同时,可能把用户的sudo权限也带走。 - 不要手改
/etc/group。必须改时用vigr,改完跑一遍grpck。 - 不要忘了重启会话。加组、改组成员后,让用户重新登录再测试,省掉半小时无意义的排查。
6.3 V10-SP1 2503升级后建议做的检查
系统升级到V10-SP1 2503之后,有几个和用户组相关的点值得顺手检查一遍。一是确认关键用户还在sudo组里,升级过程中偶尔会出现附加组列表漂移的情况;二是查看getent group plugdev、getent group netdev等桌面依赖组是否仍然存在,某些精简升级场景下PAM或设备管理策略变化会影响组成员身份;三是跑一遍sudo grpck,确保组文件本身没有历史遗留的无效引用。
最后说点我自己的习惯。我每次给用户加完组,都要求对方先注销重登,然后直接在终端执行groups确认,再用实际业务命令验证一次。三步都过了才算交付完成。别嫌繁琐,用户组这种东西,只差一个session的刷新时间,就能让“加了权限”变成“权限无效”两种完全不同的结果。系统知识看起来平平无奇,真正卡住人的永远是那些最基础的机制细节。