前言

“Permission denied”—— 每一个 Linux 用户都曾面对过的“拦路虎”。这不仅仅是一条错误信息,更是通往理解 Linux 安全核心的大门。Linux 作为一个多用户操作系统,其精髓就在于如何通过权限系统,清晰地界定“谁”可以对“什么文件”做“什么事”。

用户和用户组

主组与附加组

一个用户可以属于 一个主用户组 (Primary Group) 和 **多个附加用户组 (Supplementary/Secondary Groups)**。

  • 主用户组:用户创建文件时,文件的默认所属组就是这个组。每个用户必须有且只有一个主用户组
  • 附加用户组用户为了获得访问其他资源的权限而加入的组。比如,为了能运行 docker 命令,需要将用户加入 docker 组。

如何查看?

最简单直接的命令是 id

1
2
3
4
5
# 查看当前登录用户的组信息
id

# 查看指定用户(例如 arthur)的组信息
id arthur

示例输出及解读:

1
2
$ id arthur
uid=1001(arthur) gid=1001(arthur) groups=1001(arthur),27(sudo),998(docker)
  • uid=1001(arthur): 用户的ID和名称。
  • gid=1001(arthur): 主用户组的ID和名称。这里用户的名称和主组的名称相同,这是一种常见的配置(称为 User Private Group)。
  • groups=...: 用户所属的所有组列表,包括主组和所有附加组。
    • 1001(arthur): 他的主组。
    • 27(sudo): 他是一个附加组成员,因此他可以使用 sudo 命令。
    • 998(docker): 他也是 docker 组的附加组成员,因此他可以直接运行 docker 相关命令。

如果你只想看组名列表,可以使用 groups 命令:
$ groups arthur
arthur : arthur sudo docker


用户组下的用户

要知道一个特定的组里有哪些成员,我们可以查询 /etc/group 文件。

方法一:使用 getent 命令 (推荐)

getent 命令可以从系统数据库中获取条目,比直接 grep 文件更可靠。

1
2
# 查看 sudo 组的成员
getent group sudo

示例输出:

1
sudo:x:27:arthur,bob

这表示 sudo 组的 GID 是 27,的成员列表里有 arthurbob

方法二:直接搜索 /etc/group 文件

1
grep '^sudo:' /etc/group

输出结果和上面类似。

注意:一个用户的主组关系是在 /etc/passwd 文件中定义的,/etc/group 的成员列表里只显示作为附加组加入的用户。getent 会综合考虑这两种情况,所以结果更全面。


用户组类型

我们可以分为两大类:

系统用户组 (System Groups)

这些组通常在安装操作系统或特定软件(如数据库、Web服务器)时自动创建,用于权限管理,不代表真实的人。

  • 数量:一个最小化的服务器可能有几十个,一个功能齐全的桌面系统可能有上百个。
  • 常见名称示例
    • root: 超级管理员组。
    • sudowheel: 允许其成员使用 sudo 提升权限的组。
    • adm: 用于系统监控,其成员可以读取很多日志文件。
    • www-data (Debian/Ubuntu) 或 apache (CentOS/RHEL): Web 服务器(如 Nginx, Apache)运行所使用的组。网页文件通常需要授权给这个组。
    • docker: 允许其成员在没有 sudo 的情况下运行 Docker 命令。
    • mysql, postgres: 数据库服务使用的组。
    • systemd-journal, systemd-network: systemd 核心服务使用的组。

用户用户组 (User Groups)

这些组通常是管理员为了管理真实用户而创建的。

  • User Private Group (UPG) 模式

    • 这是现在绝大多数 Linux 发行版的默认策略。当你创建一个新用户(如 adduser arthur),系统会自动创建一个同名的组 arthur,并将作为该用户的主组。
    • 好处:默认情况下,用户创建的文件权限是 644 (rw-r–r–),所有者是 arthur,所属组也是 arthur。因为组里只有他自己,所以文件默认是私有的,非常安全。当需要协作时,再把他加入到一个共享的附加组(比如 developers)中。
  • 共享组模式

    • 在一些老系统或特定环境中,可能会创建一个像 users 这样的通用组,所有普通用户的主组都是 users。这种模式现在不太常见,因为不利于文件隐私和权限隔离。

如何查看系统上所有的组?

你可以直接查看 /etc/group 文件。

1
2
3
4
5
# 使用 less 分页查看所有组
less /etc/group

# 统计组的数量
wc -l /etc/group

你会看到一个长长的列表,大部分都是你可能不认识的系统组。

添加/移除组成员

gpasswd (group password) 是一个专门用来管理 /etc/group 的工具,在添加/移除组成员时语法非常清晰。

将用户加入组 (Add)

语法:

1
sudo usermod -aG <组名> <用户名>
1
sudo gpasswd -a <用户名> <组名>

示例:
同样是将用户 arthur 加入 docker 组。

1
sudo gpasswd -a arthur docker

输出会是:Adding user arthur to group docker

从组中移除用户 (Remove)

这是 gpasswd 的优势所在,语法非常直观。

语法:

1
sudo gpasswd -d <用户名> <组名>

示例:
现在我们想把 arthurdocker 组中移除。

1
sudo gpasswd -d arthur docker

输出会是:Removing user arthur from group docker

验证:
再次使用 id arthur 查看,你会发现 docker 组已经从他的 groups 列表中消失了

权限的基础:所有权 (Ownership)

在 Linux 中,每个文件和目录都有一个“身份标签”,定义了谁是主人。这个身份由三部分组成:

  • **用户 (User)**:文件的所有者。通常是创建该文件的用户。
  • **用户组 (Group)**:一个用户的集合。文件可以被分配给一个用户组,组内的所有用户都可能拥有特定权限。这对于团队协作非常有用。
  • **其他人 (Others)**:既不是文件所有者,也不属于文件所在用户组的任何其他用户。

可以通过 ls -l 命令来查看文件的所有权信息:

1
2
3
4
5
$ ls -l my_file.txt
-rw-r--r-- 1 arthur developers 1024 Oct 26 10:30 my_file.txt
| |
用户 用户组
(arthur) (developers)

核心权限位:r, w, x

ls -l 命令输出的开头部分 -rw-r--r-- 就是权限的直观表示。分为 4 个部分:

部分 字符 含义
第 1 位 - 代表文件类型(- 是普通文件, d 是目录, l 是链接等)
第 2-4 位 rw- 用户 (User) 的权限
第 5-7 位 r-- 用户组 (Group) 的权限
第 8-10 位 r-- 其他人 (Others) 的权限

这里的 r, w, x 分别代表三种基本权限:

  • 读 (Read - r):
    • 对于文件: 可以读取文件的内容 (例如 cat, less, cp)。
    • 对于目录: 可以列出目录中的文件和子目录列表 (例如 ls)。
  • 写 (Write - w):
    • 对于文件: 可以修改文件的内容 (例如 vim, echo >)。
    • 对于目录: 可以在目录中创建、删除、重命名文件或子目录 (例如 touch, rm, mv)。注意:删除一个文件需要其所在目录的 w 权限,而不是文件本身的 w 权限
  • 执行 (Execute - x):
    • 对于文件: 可以将文件作为程序来运行 (例如 ./my_script.sh)。
    • 对于目录: 可以进入该目录 (例如 cd)。这是访问目录下任何内容的前提

🔑 关键点:一个目录的 x 权限至关重要。即使你有目录下某个文件的 r 权限,如果没有该目录的 x 权限,你甚至无法进入目录来访问那个文件。

修改权限:chmod 命令

chmod (change mode) 是我们用来修改文件或目录权限的命令。有两种使用方式:符号模式和八进制模式

符号模式 (Symbolic Mode)

这种方式非常直观,易于理解。

  • 操作对象: u (user), g (group), o (others), a (all, 即 ugo)
  • 操作符: + (添加权限), - (移除权限), = (精确设置权限)
  • 权限: r, w, x

示例:

1
2
3
4
5
6
7
8
9
10
11
# 给用户添加执行权限
chmod u+x my_script.sh

# 移除其他人对文件的写权限
chmod o-w sensitive_data.txt

# 让用户组和用户拥有相同的权限:读和写
chmod g=rw project_plan.md

# 移除所有人的执行权限
chmod a-x my_file.txt

八进制模式 (Octal Mode)

这种方式更快捷,是系统管理员和脚本中的常用方法。将权限用数字表示:

  • r = 4
  • w = 2
  • x = 1
  • - = 0

将每组(用户、用户组、其他人)的权限数字相加,得到一个三位数的八进制代码。

常见组合:

数字 权限 (rwx) 含义
7 rwx (4+2+1) 读、写、执行
6 rw- (4+2+0) 读、写
5 r-x (4+0+1) 读、执行
4 r-- (4+0+0) 只读
0 --- (0+0+0) 无任何权限

示例:

  • chmod 755 my_directory:

    • 7 (rwx): 用户拥有所有权限。
    • 5 (r-x): 用户组可以读取和进入目录。
    • 5 (r-x): 其他人可以读取和进入目录。
    • (这是目录和脚本的常用权限)
  • chmod 644 my_file.txt:

    • 6 (rw-): 用户可以读写。
    • 4 (r--): 用户组只读。
    • 4 (r--): 其他人只读。
    • (这是普通文件的常用权限)
  • chmod 777 sensitive_data: 危险! 任何人都有权读、写、执行。请极度谨慎使用。

改变身份:chownchgrp

  • chown (change owner): 用来改变文件或目录的所有者(用户和用户组)。
  • chgrp (change group): 用来单独改变文件或目录的用户组。

示例:

1
2
3
4
5
6
7
8
9
10
11
# 将文件所有者改为 alice
sudo chown alice my_file.txt

# 将文件所有者改为 alice,用户组改为 developers
sudo chown alice:developers my_file.txt

# 只改变文件的用户组为 developers
sudo chgrp developers my_file.txt

# 递归改变整个目录及其内容的所有权
sudo chown -R arthur:developers /opt/project_alpha

-R--recursive 选项非常强大,表示对目录内的所有文件和子目录都执行相同操作。

特殊权限 setuid, setgid, sticky bit

除了基本的 rwx,Linux 还提供了三种特殊的权限位。

SUID (Set User ID)

  • 表现形式: 在用户执行位上显示为 s (或 S),例如 rwsr-xr-x
  • 作用: 当一个设置了 SUID 的可执行文件被运行时,该进程将以文件所有者的权限来运行,而不是以执行者的权限。
  • 经典案例: /usr/bin/passwd 命令。普通用户执行 passwd 时,需要修改 /etc/shadow 这个 root 用户才能写入的文件。passwd 命令的所有者是 root 且设置了 SUID,所以任何用户运行时,都临时获得了 root 权限来修改密码文件。
  • 设置方法: chmod u+s filenamechmod 4755 filename

SGID (Set Group ID)

  • 表现形式: 在用户组执行位上显示为 s (或 S),例如 rwxr-sr-x
  • 作用:
    1. 对文件: 与 SUID 类似,运行时进程将获得文件所属用户组的权限
    2. 对目录 (更常用): 在一个设置了 SGID 的目录中创建的任何新文件或子目录,其用户组将自动继承该父目录的用户组,而不是创建者的默认用户组。这对于团队共享文件夹非常有用!
  • 设置方法: chmod g+s dirnamechmod 2775 dirname

Sticky Bit (粘滞位)

  • 表现形式: 在其他人执行位上显示为 t (或 T),例如 rwxrwxrwt
  • 作用: 只对目录有效。在一个设置了粘滞位的目录中,即使用户对该目录有写权限,他也只能删除或重命名自己拥有的文件,而不能删除或重命名其他用户的文件。
  • 经典案例: /tmp 目录。所有用户都可以在 /tmp 中创建文件,但粘滞位确保了你不能删除别人的临时文件。
  • 设置方法: chmod +t dirnamechmod 1777 dirname

总结

  • 最小权限原则: 只给予用户完成任务所需的最小权限。
  • 默认权限: 755 适用于目录和可执行文件,644 适用于普通文件。
  • 谨慎使用 777: 避免使用 chmod 777,会带来巨大的安全风险。
  • 理解 x 对目录的意义: 无法 cd 进一个目录,十有八九是 x 权限问题。
  • 善用 SGID: 在团队协作目录上设置 SGID 可以极大地方便文件管理。

为服务创建专用的、权限受限的系统用户

创建一个名为 navidrome 的系统用户,不能登录,并且没有家目录。

1
sudo useradd --system --shell /sbin/nologin --no-create-home navidrome
  • sudo: 需要管理员权限。
  • useradd: 创建用户的命令。
  • --system: 告诉系统这是一个服务/系统账户。
  • --shell /sbin/nologin: 禁止登录。
  • --no-create-home: 不要在 /home 下创建 navidrome 目录。
  • navidrome: 新用户的名字。