操作Linux时经常会遇到权限不足的问题,这就需要对Linux系统的权限机制有比较全面的了解,本文尝试总结Linux系统所具有的各种权限控制机制,然后介绍下使用sudo提权的操作。
一、Linux上的各种权限
※ 基于用户和组的权限
Linux系统最基本的权限系统,是基于用户和组的权限系统,每个文件都根据用户、用户组和其他设定读、写、可执行权限。
用户和用户组
root 用户:Linux 的终极管理员,拥有一切权限。
普通用户:普通用户一般只对自己的home路径有权限,但可以配置允许普通用户可以提权到root权限,也就是允许执行sudo,后文再介绍。
用户组:用户分组,快速配置相同的权限,一般一个用户都有一个和用户同名的用户组,比如用户thht也属于thht用户组。
用户信息保存在 /etc/passwd 中,为安全考虑,用户密码并没有保存在这里,实际保存在 /etc/shadow 文件中。
用户组信息保存在 /etc/group 中,同样组密码保存在单独的文件中 /etc/gshadow 中。
管理用户、用户组相关命令:useradd userdel usermod passwd groupadd groupdel groupmod
文件和目录,都是属于某个用户或某个用户组;
文件的读、写、可执行权限就是字面上的意思;
目录的 " 读、写 " 控制对象是目录内部文件列表,没有读权限不能获取文件列表,没有写权限不能重命名文件;"执行 " 指内部的文件是否可以“读写可执行”。
二进制 100 表示 " 读 " r权限(十进制 4)
二进制 010 表示 " 写 " w权限(十进制 2)
二进制 001 表示 " 可执行"x权限(十进制 1)
三个权限组合到一起就是rwx,没有权限的用-,比如r-x代表没有写权限。
用一位十进制数即可代替三个字母表示权限,也就是上边三个权限十进制数的相加组合,比如7权限就是4+2+1,对应二进制111,表示拥有所有权限。
一般用三位十进制数分别表示用户、组、和其它用户的权限。修改权限的命令是 chmod:
chmod 644 a.txt
代表用户有读写权限,组成员和其他人只有读权限。
umask :创建文件时生成权限的策略,是创建文件时使用权限 6 减去 umask 值;创建路径时使用 7 减去 umask 值。
ls -l 显示的权限分别是文件类型、属主、组、其他人的权限:
※特殊权限
SUID: 能够让二进制程序的执行者临时拥有属主的权限。
SGID: 有两种应用场景,当对二进制程序进行设置时,能够让执行者临时获取到文件所有组的权限;而对目录进行设置时,则是让目录内新创建的文件自动继承该目录原有用户组的名称。
SBIT: 粘滞位,当时文件使用时,该文件只能被其所有者执行删除操作;当对某个目录使用时,该目录中的文件就只能被其所有者执行删除操作。
chmod u+s /tmp/test # 设置 SUID 权限
chmod u-s /tmp/test # 取消 SUID 权限
chmod g+s /tmp/test # 设置 GUID 权限
chmod g-s /tmp/test # 取消 GUID 权限
chmod o+t /tmp/test # 设置 SBIT 权限
chmod o-t /tmp/test # 取消 SBIT 权限
SUID、SGID 与 SBIT 也有对应的数字法表示,分别也是 4、2、1,也就是用数字配置权限最左边增加一位,用四位数:
chmod 7777 /tmp/test # 设置所有权限
被设置的特殊权限会在 ls 权限列表中的“可执行”位的位置体现出来:
root@qc:~# touch test1 test2
root@qc:~# chmod 7000 test1
root@qc:~# chmod 7111 test2
root@qc:~# ls -l test1 test2
---S--S--T 1 root root 0 Oct 22 10:12 test1 # 大写代表没有可执行权限
---s--s--t 1 root root 0 Oct 22 10:12 test2 # 小写代表有可执行权限
root@qc:~#
用户权限位:S代表SUID,s代表SUID+x
组权限位:S代表SGID,s代表SGID+x
其他权限位:T代表SBIT,t代表SBIT+x
也就是小写表示同时有可执行权限,大写表示没有可执行权限。
※隐藏属性权限
读写可执行之外的功能权限,比如锁住一个文件不允许修改的权限 i;只允许以 append 方式打开文件的权限属性 a 等等。
相关命令:chattr 、lsattr。详见 man chattr
※基于文件系统的 FACL 控制
基本的权限只能对文件或目录的所有者、组、其他人配置权限,FACL 可以指定多个用户和分组独立配置权限。目前绝大部分的文件系统都有支持 ACL 的功能,包括 ReiserFS, ext2/ext3/ext4, JFS, XFS 等等。
有些文件系统挂载时自动开启,显式开启可以在 /etc/fstab 文件的 options 列添加acl。
相关命令 getfacl、setfacl
setfacl -m u:hare:rw graph.svg #给用户hare设置rw权限
setfacl -m g:sudo:rwx graph.svg #给sudo组设置rwx权限
setfacl -x g:sudo graph.svg #删除sudo的组权限
root@aliecs:/tmp# ls -l graph.svg # 配置了ACL的文件权限最后显示+号
-rw-r--r--+ 1 root root 20776 Jan 13 11:57 graph.svg
root@aliecs:/tmp# getfacl graph.svg #查看文件权限配置
# file: graph.svg
# owner: root
# group: root
user::rw-
user:hare:rw- #effective:r--
group::r--
group:sudo:rwx #effective:r--
mask::r--
other::r--
※ 特权用户与 Linux Capacity
除了文件权限,Linux 内核对某些功能进行了能力限制。比如 CAP_CHOWN(修改文件属主的权限)、CAP_KILL(发送信号的权限)、CAP_NET_ADMIN(网络管理的权限)等。
在老系统上,按是否特权用户(UID 为 0,即 root 用户)决定这些权限能否使用。Linux 内核 2.2 之后,把这些能力变成独立配置项,授权给线程,内核提供配置权限的接口,启动新线程时会根据线程的用户等信息决定它拥有的权限。
root 用户运行的程序一般会拥有这些权限,普通用户则没有。
查看一个进程拥有的权限:
方法 1:使用进程 id
root@aliecs:~# getpcaps 1397267
1397267: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+eip
方法 2:通过 /proc 路径,查看进程状态
root@aliecs:~# cat /proc/1397267/status | grep Cap
CapInh: 00000000a80425fb
CapPrm: 00000000a80425fb
CapEff: 00000000a80425fb
CapBnd: 00000000a80425fb
CapAmb: 0000000000000000
root@aliecs:~# capsh --decode=00000000a80425fb
0x00000000a80425fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
相关知识参考 man capabilities
※针对进程的权限
SELinux 和 AppArmor 等权限控制机制,是在进程层面管控文件系统资源的访问。如果配置了相关限制,即使 root 用户也可报权限错误。它们对于所有的文件,目录,端口这类的资源的访问是基于策略设定的。
具体如何使用请自行搜索相关文档。
二、用户切换和提权
首先提醒下读者,切换用户或提权到 root 也解决不了的问题,需要考虑上文所提到的扩展权限、 SELinux 等机制。
su 用于切换用户的登录身份
省略参数默认切换到 root 用户,只有知道 root 密码或拥有 root 权限的用户才可以使用。已经是 root 用户切换到其他用户不需密码,其他用户切换需要目标账号密码。
经常用于普通用户切到 root 用户,获得 root 权限。常用的参数是 --login 或 -l ,再简写成一个 - 也可。详情见 man su
sudo 是以其他用户身份执行命令
一般是获取 root 权限执行命令。需要输入密当前用户的密码,这种方式可以不用知道 root 密码就可以提权。但允许哪些用户可以提权执行命令需要提前配置,默认配置文件是 /etc/sudoers,也可以通过 LDAP 配置。
注:编辑配置文件应该使用 visudo,会自动调用系统默认编辑器,有语法检查,防止出错。指定文件使用 -f 参数。
sudoers 配置文件内容
下边这个文件是一般系统的初始化配置,主要是配置了 root 用户和管理员用户组(admin、sudo 组)的权限。
Defaults env_reset
Defaults mail_badpass
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
root ALL=(ALL:ALL) ALL
%admin ALL=(ALL) ALL
%sudo ALL=(ALL:ALL) ALL
#includedir /etc/sudoers.d
Defaults 部分
上例中:
env_reset 重置环变量,移除执行 sudo 命令用户的环境变量; mail_badpass 执行 sudo 时输错密码,会给管理员发邮件,默认是 root; secure_path 重置安全的 PATH 路径,不会使用用户的;
具体格式:
Defaults Parameter = Value # 可以用+= -=配置Value细节
Defaults ! Parameter # 关闭参数
# 也可以指定影响范围,相关的List见下文
Defaults @ Host_List Parameter = Value # 对Host_List配置
Defaults : User_List Parameter = Value # 对User_List配置
Defaults ! Cmnd_List Parameter = Value # 对Cmnd_List配置
Defaults > Runas_List Parameter = Value # 对Runas_List配置
更多参数及具体细节,可以参考 man sudoers 的 SUDOERS OPTIONS 部分。
列表别名
配置文件主要描述的是”某个用户在哪个主机上能以什么身份运行什么命令 “,这其中有四个对象可以预定义一个别名,每个别名定义一组对象。
语法格式 Alias_Type NAME = item1, item2, ...
Alias_Type 有四种:User_Alias, Host_Alias, Runas_Alias, Cmnd_Alias
NAME 只能由大写字母、数字和下划线组成,必须以大写字母开头。已经定义好的别名可以作为同类的 item 使用。
item 前加 "!" 代表取反。NAME=item 可以用冒号 ":" 分隔一次定义多个。
具体的 item 格式分别为:
User_Alias:用户名,用户id(#开头),用户组(%开头),组id(%#开头),网络组(+开头),非Linux用户组名格式(%:开头),非Linux用户组名id格式(%:#开头)。
例:
User_Alias USERS1 = millert, mikef, dowdy
User_Alias USERS2 = bostley, ! jwfox, crawl :\
USERS3 = will, wendy, wim
User_Alias USERS4 = wubbi, USERS1
Runas_Alias:item格式和User_Alias一样。
Host_Alias:主机名,IP地址,网段(可带掩码),网络组。
例:
Host_Alias SPARC = bigtime, eclipse, moet, anchor :\
SGI = grolsch, dandelion, black :\
ALPHA = widget, thalamus, foobar :\
HPPA = boa, nag, python
Host_Alias CUNETS = 128.138.0.0/255.255.0.0
Host_Alias CSNETS = 128.138.243.0, 128.138.204.0/24, 128.138.242.0
Host_Alias SERVERS = master, mail, www, ns
Host_Alias CDROM = orion, perseus, hercules
Cmnd_Alias:可执行文件,可执行文件的路径(不包含子路径),"sudoedit"(sudo -e 文件路径,编辑文件而不是执行命令)。
其中可执行文件前,可加数字指纹 "sha224" ':' xxxxx (V1.8.7之后的版本支持);后边不加参数代表用户可自由使用参数;后边加参数代表只允许固定的参数;加 "" 代表不允许带参数。
例:
Cmnd_Alias DUMPS = /usr/bin/mt, /usr/sbin/dump, /usr/sbin/rdump,\
/usr/sbin/restore, /usr/sbin/rrestore,\
sha224:0GomF8mNN3wlDt1HD9XldjJ3SNgpFdbjO1+NsQ== \
/home/operator/bin/start_backups
Cmnd_Alias KILL = /usr/bin/kill
Cmnd_Alias PRINTING = /usr/sbin/lpc, /usr/bin/lprm
Cmnd_Alias SHUTDOWN = /usr/sbin/shutdown
Cmnd_Alias HALT = /usr/sbin/halt
Cmnd_Alias REBOOT = /usr/sbin/reboot
Cmnd_Alias SHELLS = /usr/bin/sh, /usr/bin/csh, /usr/bin/ksh,\
/usr/local/bin/tcsh, /usr/bin/rsh,\
/usr/local/bin/zsh
Cmnd_Alias SU = /usr/bin/su
Cmnd_Alias PAGERS = /usr/bin/more, /usr/bin/pg, /usr/bin/less
用户规范说明部分
这一部分是最核心的授权描述分为三个部位。
第一位:对谁授权,可以用 User_Alias 别名; 第二位:主机名,一个 sudoers 文件可以复制给多个设备共用,此项指定哪个设备上配置生效,可以用 Host_Alias 别名,ALL 为所有设备的别名。 第三位:可以执行的命令,这一位可以继续分为 3 部分
1)作为 "( 用户 : 分组 )" 执行命令,可以用 Runas_Alias 别名,ALL 代表所有用户或分组,可完全省略或省略部分,完全省略则默认为 root。
2)功能选项或标签,比如 NOTAFTER=2023022008Z , NOPASSWD: , 可省略。
3)允许执行的命令、命令路径,可以用 Cmnd_Alias 别名,ALL 代表所有命令。
比如 root ALL=(ALL:ALL) ALL 就代表 root 用户,可以在任何主机上,以任何用户或用户组,执行任何命令,这一条保证了 root 用户执行 sudo 命令时也可以得到运行。
凡是可以用别名的地方,都可以直接写组成别名的 item 项目。
例:
User_Alias GROUPONE = abby, brent, carl
User_Alias GROUPTWO = brent, doris, eric,
User_Alias GROUPTHREE = doris, felicia, grant
# 允许GROUPONE以root身份执行apt-get update
GROUPONE ALL = /usr/bin/apt-get update
# 允许GROUPTHREE以root身份执行开关机相关命令
Cmnd_Alias POWER = /sbin/shutdown, /sbin/halt, /sbin/reboot, /sbin/restart
GROUPTHREE ALL = POWER
# 允许GROUPONE 以www-data或apache身份执行命令
Runas_Alias WEB = www-data, apache
GROUPONE ALL = (WEB) ALL
# 执行命令时免密码
GROUPONE ALL = NOPASSWD: /usr/bin/*
# 多个命令,部分免密码
GROUPTWO ALL = NOPASSWD: /usr/bin/updatedb, PASSWD: /bin/kill
# 不允许命令内执行新的命令(在less内,!后加命令可以执行其他命令)
username ALL = NOEXEC: /usr/bin/less
sudo 相关命令
# sudo 使用自己的密码验证,拥有root权限后执行su就可以root登录了
sudo su
# 指定作为哪个用户
sudo -u run_as_user command
# 指定作为哪个分组
sudo -g run_as_group command
# 查看允许执行的命令
sudo -l
# sudo输入密码后会保持一段时间不用重复输入
sudo -k # 不保持,立即超时
sudo -v # 续期
更多细节参考 man sudoers、man sudo