关于Linux的账号管理,有两个数字最为重要:
LInux通过这两个数字识别系统中的用户以及群组。
要查看当前账号的UID与GID,可以:
[icexmoon@xyz ~]$ id
uid=1000(icexmoon) gid=1000(icexmoon) 组=1000(icexmoon),10(wheel) 环境=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
查看指定行号的UID与GID:
[icexmoon@xyz ~]$ id root
uid=0(root) gid=0(root) 组=0(root)
事实上用户的UID和GID等信息都保存在几个账号和群组相关的配置文件中,其中最重要的是/etc/passwd
:
[icexmoon@xyz ~]$ cat /etc/passwd | grep icexmoon
icexmoon:x:1000:1000:icexmoon:/home/icexmoon:/bin/bash
其中第三个字段是用户的UID,第四个字段是用户的主要群组(初始化群组)的GID。
在用户登录Linux的时候,用户验证的大概流程如下:
/etc/passwd
文件中,如果有,则读取其对应的UID和GID,以及登录用的shell和账号的家目录等。/etc/shadow
文件比对用户输入的密码是否正确。shell
进行登录。/etc/passwd
现在看/etc/passwd
的详细结构:
[icexmoon@xyz ~]$ head -n 5 /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
该文件由:
分隔,每一个字段的含义为:
/etc/shadow
中,所以当前该字段中会用x
字符填充。root
用户,1~999
为系统账号,1000~60000
为可以设置的一般性用户账号。/sbin/nologin
,所以无法登录。/etc/shadow
用户的密码保存在/etc/shadow
中:
[root@xyz ~]# head -n 5 /etc/shadow
root:$6$h5eXZCiKw8ucXyB2$PxxRgKneIDs2TWIpg916ugAfGCfqNf/HJHiXjy0PTPtUmY.pR/e2cUMpjRBqbx5Y0AHNJtjxAf3aOUTek3DpO.::0:99999:7:::
bin:*:18353:0:99999:7:::
daemon:*:18353:0:99999:7:::
adm:*:18353:0:99999:7:::
lp:*:18353:0:99999:7:::
同样以:
分隔,字段对应的内容为:
!
字符,如果要恢复登录就取消,很方便。如果一般用户忘记了密码,很好解决,只要用root
或者其它管理员账号修改即可,如果root
账号的密码忘了,且没有其它的管理员账号可以使用,就比较麻烦了,要么通过单人维护模式之类的方式启动系统,此时会获得root
权限,进行密码重置,要么可以用启动U盘之类的启动系统,用挂载文件系统的方式挂载/etc/shadow
所在分区,然后清空其中的root
密码字段,然后重启系统后就会提示设置root
的密码。
如果要查看Linux对密码加密使用的是何种算法,可以:
[root@xyz ~]# authconfig --test | grep hashing
password hashing algorithm is sha512
/etc/group
用户组相关的信息保存在/etc/group
中:
[root@xyz ~]# head -n 5 /etc/group
root:x:0:
bin:x:1:
daemon:x:2:
sys:x:3:
adm:x:4:
这些字段的含义为:
/etc/gshadow
root:x:0:user1,user2
就表明user1
和user2
两个用户包含在root
用户组中,一个用户可以加入多个用户组,此外,如果某个用户的主用户组是当前的用户组,则不需要写入这里。用户的主用户组,或者说初始化用户组(initial group)是创建用户时所关联的用户组,是不需要将用户名写入/etc/group
中的相关字段的:
[root@xyz ~]# grep icexmoon /etc/passwd /etc/shadow /etc/group
/etc/passwd:icexmoon:x:1000:1000:icexmoon:/home/icexmoon:/bin/bash
/etc/shadow:icexmoon:$6$vGNuDgQdXLIv4U19$mnkT6XkbUOAAO9jxxBfzXbRfS6ZA9LpR0fDwRJmfQ2cZ7PyMF18aGosW00KXjlcsv9GbzDpzSqgvqQr5E51aW/::0:99999:7:::
/etc/group:wheel:x:10:icexmoon
/etc/group:icexmoon:x:1000:icexmoon
不过事实上这里是有的:
/etc/group:icexmoon:x:1000:icexmoon
,是CentOS版本的问题?
使用groups
命令可以查看用户所有关联的用户组:
[icexmoon@xyz ~]$ groups
icexmoon wheel
其中第一个为用户的有效用户组(effective group),如果创建新文件,所用的用户组信息就会是这个有效用户组:
[icexmoon@xyz tmp]$ touch test_group
[icexmoon@xyz tmp]$ ll test_group
-rw-rw-r--. 1 icexmoon icexmoon 0 8月 19 21:43 test_group
使用newgrp
命令可以切换有效用户组:
[icexmoon@xyz tmp]$ newgrp wheel
[icexmoon@xyz tmp]$ groups
wheel icexmoon
[icexmoon@xyz tmp]$ touch test_grou2
[icexmoon@xyz tmp]$ ll test_grou*
-rw-r--r--. 1 icexmoon wheel 0 8月 19 21:45 test_grou2
-rw-rw-r--. 1 icexmoon icexmoon 0 8月 19 21:43 test_group
[icexmoon@xyz tmp]$ exit
exit
newgrp
会打开一个子shell
,并改变有效用户组,此时新建文件会使用新的有效用户组。需要注意的是因为是新的子shell
,所以在执行完相关操作后需要使用exit
退出,并返回之前所在的shell
中。
/etc/gshadow
/etc/gshadow
主要的用途是保存用户组的密码:
[root@xyz ~]# head -n 5 /etc/gshadow
root:::
bin:::
daemon:::
sys:::
adm:::
这些字段的含义是:
/etc/group
相同)所谓的用户组管理员,就是可以帮助root
管理对应的用户组,进行增加和删除用户,如果Linux主机上的用户很多,且频繁需要管理用户组,就可能需要设置用户组管理员来帮助管理,不过现在已经很少需要这么做了。
使用useradd
就可以添加一个新用户:
[root@xyz ~]# useradd user1
[root@xyz ~]# ls -ald /home/user1
drwx------. 3 user1 user1 78 8月 20 18:26 /home/user1
[root@xyz ~]# grep user1 /etc/passwd /etc/shadow /etc/group
/etc/passwd:user1:x:1001:1001::/home/user1:/bin/bash
/etc/shadow:user1:!!:18859:0:99999:7:::
/etc/group:user1:x:1001:
可以看到新用户创建后,会自动创建相应的家目录(一般是/home/xxx
),此外还会在前边介绍的几个关键的用户和用户组相关的配置文件中添加相应的信息。
需要注意的是,/etc/shadow
中添加的新记录中的密码字段是!!
,意味着该用户的密码还未设置,所以需要设置密码后才能使用,可以用root
进行设置:
[root@xyz ~]# passwd user1
更改用户 user1 的密码 。
新的 密码:
无效的密码: 密码未通过字典检查 - 它基于字典单词
重新输入新的 密码:
passwd:所有的身份验证令牌已经成功更新。
这里我使用的是一个常见的简单密码,所以被提示未通过字典检查,但是
root
依然可以通过重复两次的方式强行设置。
默认情况下useradd
会给新用户指定一个UID和GID,其中GID是为新用户单独创建的一个新的用户组。如果你有特殊需要,也可以为新用户指定一个UID和一个已存在的用户组作为其主用户组:
[root@xyz ~]# useradd -u 1500 -g users user2
[root@xyz ~]# ls -ald /home/user2
drwx------. 3 user2 users 78 8月 20 18:37 /home/user2
[root@xyz ~]# grep user2 /etc/passwd /etc/shadow /etc/group
/etc/passwd:user2:x:1500:100::/home/user2:/bin/bash
/etc/shadow:user2:!!:18859:0:99999:7:::
上面说的是用于普通用户登录和使用Linux主机的一般账号,此外还可以添加系统账号:
[root@xyz ~]# useradd -r user3
[root@xyz ~]# ls -ald /home/user3
ls: 无法访问/home/user3: 没有那个文件或目录
[root@xyz ~]# grep user3 /etc/passwd /etc/shadow /etc/group
/etc/passwd:user3:x:988:982::/home/user3:/bin/bash
/etc/shadow:user3:!!:18859::::::
/etc/group:user3:x:982:
使用useradd -r
可以添加系统账号,可以看到分配的UID是988
,小于1000,此外,因为系统账号一般用于运行程序等,并不会被用户登录和使用,所以没有创建对应的家目录。
可以通过useradd -D
查看useradd
命令创建用户时的默认值:
[root@xyz ~]# useradd -D
GROUP=100
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/bash
SKEL=/etc/skel
CREATE_MAIL_SPOOL=yes
其实这些内容是从
/etc/default/useradd
这个文件读取的。
这些默认值的含义为:
GROUP=100
新建用户时候分配的初始用户组是100
。但是CentOS或Redhat系列发行版中,实际上并不会使用这个设置,而是会为新用户创建同名的一个新的用户组作为初始用户组,这种机制称为“私有用户组机制”,而使用这个设置并为新用户指定同一个用户组作为初始用户组的方式称为“公共用户组机制”。
HOME=/home
为新用户创建的家目录的位置,一般为/home/xxx
。
INACTIVE=-1
这个默认值对应/etc/shadow
中的第7个字段“密码过期后的宽限天数”,-1
表示永不失效,0
表示立即失效,其它值表示具体的宽限天数。
EXPIRE
对应/etc/shadow
中的第8个字段,账号会在到达该天数后失效。
SHELL=/bin/bash
新用户默认使用的shell
程序,如果为了安全考虑,希望新用户默认都不能登录,可以将这个值改为/sbin/nologin
。
SKEL=/etc/skel
新用户家目录的模板目录,在创建新用户的时候,会使用这里的模板目录为新用户创建家目录,比如,如果你希望新用户的家目录有一些基础的用户手册或者shell脚本,可以在/etc/skel
中设置好对应的文件,这样新建的用户的家目录里也会有一份相关文件的拷贝。
CREATE_MAIL_SPOOL=yes
是否为新用户创建邮箱,也就是/var/spool/mail/xxx
。
除了这些默认值以外,useradd
还需要参考其他的配置文件,比如/etc/login.defs
:
[root@xyz ~]# cat /etc/login.defs | sed '/^#.*$/d' | sed '/^$/d'
MAIL_DIR /var/spool/mail
PASS_MAX_DAYS 99999
PASS_MIN_DAYS 0
PASS_MIN_LEN 5
PASS_WARN_AGE 7
UID_MIN 1000
UID_MAX 60000
SYS_UID_MIN 201
SYS_UID_MAX 999
GID_MIN 1000
GID_MAX 60000
SYS_GID_MIN 201
SYS_GID_MAX 999
CREATE_HOME yes
UMASK 077
USERGROUPS_ENAB yes
ENCRYPT_METHOD SHA512
这个文件包含这些信息:
MAIL_DIR
:邮箱目录PASS_MAX_DAYS
:密码相关设置中可以设置的最大天数,所以新用户的密码更新天数为99999
。PASS_MIN_DAYS
:密码相关设置的最小天数PASS_MIN_LEN
:密码最小长度。因为新版Linux都已使用PAM进行登录验证,所以这里的值已不再起作用。UID_MIN
:一般用户可以设置的最小UID值。UID_MAX
:一般用户可以设置的最大UID值。SYS_UID_MIN
:系统账户可以设置的最小UID值。SYS_UID_MAX
:系统账户可以设置的最大UID值。GID_MIN
:新建的用户组可以设置的最小GID值。GID_MAX
:新建的用户组可以设置的最大GID值。CREATE_HOME
:是否默认为新用户创建家目录。UMASK
:创建家目录时使用的umask,因为这里是077
,所以创建的目录权限自然是700
。USERGROUPS_ENAB
:使用userdel
删除用户时是否会删除其初始群组。ENCRYPT_METHOD
:密码加密算法。使用passwd
命令可以修改密码,特别的,root
可以修改所有用户的密码,而一般用户只可以修改自己的密码。此外需要注意的是,如果root
修改他人的密码,passwd
后需要写上对应的用户名,否则就是修改自己的密码。
新的Linux发行版使用PAM进行登录验证,所以一般会对密码有一些基本要求,否则是不允许设置该密码的(root
除外):
123456
或password
之类的,系统有一个常见简单密码本,会进行比对。这里推荐一个大学时某网络安全老师教我们的密码生成方法:想一句话,比如”Today is a good day“,则可以取每个单词的首字母作为密码,即
Tiagd
,这种密码看起来毫无规律,但实际上只要你记住那句话就能记住密码,这大概就是所谓的容易记住的无规律密码吧。而且这种方法可以有很多衍生版本,比如用拼音,或者不取首字母,取尾字母等。
新版的CentOS为passwd
命令加入了一个新的参数--stdin
,可以结合管道命令来显式地修改密码:
[root@xyz ~]# echo 'password123' | passwd --stdin user2
更改用户 user2 的密码 。
passwd:所有的身份验证令牌已经成功更新。
使用这种方式可以通过Shell
脚本批量创建用户,并设置密码,但缺点是不安全,黑客可以通过命令历史记录看到这些信息(当然也可以执行完脚本后手动清除历史记录)。
如果想查看密码的相关设置,可以:
[root@xyz ~]# passwd -S user2
user2 PS 2021-08-20 0 99999 7 -1 (密码已设置,使用 SHA512 算法。)
这里打印的信息对应/etc/shadow
的相关字段,比如上边打印的信息就代表:user2
账号的密码最近一次修改时间为2021-08-20
,密码可以随时修改,密码会在99999
天后过期,过期前7
天会提醒用户,账号永远不会失效。
可以对密码的这些设置进行修改:
[root@xyz ~]# passwd -x 60 -i 10 user2
调整用户密码老化数据user2。
passwd: 操作成功
[root@xyz ~]# passwd -S user2
user2 PS 2021-08-20 0 60 7 10 (密码已设置,使用 SHA512 算法。)
现在user2
账号被要求在修改密码60天后再次修改密码,如果过期超过10天账户将失效。
之前在介绍/etc/shadow
时说过可以通过在密码字段前添加!
的方式让用户无法进行登录,起到临时限制登录的目的,事实上不需要手动修改/etc/shadow
文件,可以:
[root@xyz ~]# passwd -l user2
锁定用户 user2 的密码 。
passwd: 操作成功
[root@xyz ~]# passwd -S user2
user2 LK 2021-08-20 0 60 7 10 (密码已被锁定。)
[root@xyz ~]# grep user2 /etc/shadow
user2:!!$6$vsrs7fll$UAzw.3Gsu5hODBU/6UOTVy14M6M06NTZXW7//k3MXRYtG7mLwq3K.pWgOHlky1hD692uNvGjMBFUdMGqTr1iO1:18859:0:60:7:10::
可以看到对应的密码字段前被添加了两个!
,现在user2
就不能登录了。
要解除账户锁定也很简单:
[root@xyz ~]# passwd -l user2
锁定用户 user2 的密码 。
passwd: 操作成功
[root@xyz ~]# passwd -S user2
user2 LK 2021-08-20 0 60 7 10 (密码已被锁定。)
[root@xyz ~]# grep user2 /etc/shadow
user2:!!$6$vsrs7fll$UAzw.3Gsu5hODBU/6UOTVy14M6M06NTZXW7//k3MXRYtG7mLwq3K.pWgOHlky1hD692uNvGjMBFUdMGqTr1iO1:18859:0:60:7:10::
chage
(change)命令主要用于修改用户密码的过期信息,也可以用来查看密码过期信息的详情:
[root@xyz ~]# chage -l user2
最近一次密码修改时间 :8月 20, 2021
密码过期时间 :10月 19, 2021
密码失效时间 :10月 29, 2021
帐户过期时间 :从不
两次改变密码之间相距的最小天数 :0
两次改变密码之间相距的最大天数 :60
在密码过期之前警告的天数 :7
chage
有一个用途比较有用:要求新建的用户在第一次登录后必须修改密码:
[root@xyz ~]# useradd chagetest
[root@xyz ~]# echo "chagetest" | passwd --stdin chagetest
更改用户 chagetest 的密码 。
passwd:所有的身份验证令牌已经成功更新。
[root@xyz ~]# chage -d 0 chagetest
[root@xyz ~]# chage -l chagetest
最近一次密码修改时间 :密码必须更改
密码过期时间 :密码必须更改
密码失效时间 :密码必须更改
帐户过期时间 :从不
两次改变密码之间相距的最小天数 :0
两次改变密码之间相距的最大天数 :99999
在密码过期之前警告的天数 :7
看到没,出现了密码必须更改
的字样,现在使用chagetest
账号通过ssh
连接进行登录:
❯ ssh [email protected]
[email protected]'s password:
You are required to change your password immediately (root enforced)
WARNING: Your password has expired.
You must change your password now and login again!
更改用户 chagetest 的密码 。
为 chagetest 更改 STRESS 密码。
(当前)UNIX 密码:
新的 密码:
重新输入新的 密码:
passwd:所有的身份验证令牌已经成功更新。
Connection to 192.168.1.105 closed.
在使用初始密码登录后会被强制要求修改密码,且修改完后会立即退出登录,必须使用修改后的密码再重新登录后才能正常使用系统。
这种方式对系统管理员批量创建账号,并使用一个随机密码作为初始密码的情况很有用,可以在相应的用户收到自己的账号和初始密码登录后让其修改为自己习惯的密码,并避免一些可能的密码泄露问题。
如果在创建完用户后需要对用户信息进行修改,可以使用usermod
命令:
[root@xyz ~]# usermod -c 'test_account' user2
[root@xyz ~]# grep user2 /etc/passwd
user2:x:1500:100:test_account:/home/user2:/bin/bash
这里使用-c
参数给user2
添加了一段描述性文字。
此外usermod
也可以修改密码过期相关的设置:
[root@xyz ~]# usermod -e '2021-12-31' user2
[root@xyz ~]# chage -l user2
最近一次密码修改时间 :8月 20, 2021
密码过期时间 :10月 19, 2021
密码失效时间 :10月 29, 2021
帐户过期时间 :12月 31, 2021
两次改变密码之间相距的最小天数 :0
两次改变密码之间相距的最大天数 :60
在密码过期之前警告的天数 :7
这里通过-e
参数给账户添加了一个账户过期时间。
userdel
命令的用途很明确,就是删除账号:
[root@xyz ~]# userdel -r user2
[root@xyz ~]# ll /home/user2
ls: 无法访问/home/user2: 没有那个文件或目录
参数-r
的用途是将用户的家目录一同删除。
对于账户的删除操作需要谨慎,如果只是让账户失效的话,可以通过将账户的过期时间(/etc/shadow
中第8个字段)设置为0
的方式实现。因为userdel
操作可能会删除用户相关的文件,可能会导致一些依赖于该用户的程序无法正常运行,所以如果一定要执行用户删除操作,请先通过find / -user username
之类的方式查询该用户相关的文件,确定的确可以删除后再进行删除用户的操作。
之前介绍过,通过id
命令可以查询用户和用户组的相关信息:
[root@xyz ~]# id
uid=0(root) gid=0(root) 组=0(root) 环境=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[root@xyz ~]# id icexmoon
uid=1000(icexmoon) gid=1000(icexmoon) 组=1000(icexmoon),10(wheel)
finger
同样可以查询用户相关信息,这个命令一般不会默认安装,所以需要先安装:
[root@xyz ~]# yum install finger
已加载插件:fastestmirror, langpacks
Loading mirror speeds from cached hostfile
* base: mirrors.aliyun.com
* extras: mirrors.aliyun.com
使用finger
查看用户信息:
[root@xyz ~]# finger icexmoon
Login: icexmoon Name: icexmoon
Directory: /home/icexmoon Shell: /bin/bash
On since 五 8月 20 18:16 (CST) on pts/0 from icexmoon-book
1 second idle
No mail.
No Plan.
包含以下信息:
Login
:账号名Name
:描述文字Directory
:家目录Shell
:登录用Shell
~/.plan
的内容测试一下,写入一个任务计划并使用finger
查看:
[icexmoon@xyz ~]$ echo "This is a test plan." > ~/.plan
[icexmoon@xyz ~]$ finger icexmoon
Login: icexmoon Name: icexmoon
Directory: /home/icexmoon Shell: /bin/bash
On since 五 8月 20 18:16 (CST) on pts/0 from icexmoon-book
3 seconds idle
No mail.
Plan:
This is a test plan.
此外,finger
还可以查看当前Linux主机上的登录的用户:
[icexmoon@xyz ~]$ finger
Login Name Tty Idle Login Time Office Office Phone Host
chagetest pts/1 Aug 20 21:01 (icexmoon-book)
icexmoon icexmoon pts/0 Aug 20 18:16 (icexmoon-book)
chfn(change finger)的用途是修改finger
命令查看的相关个人信息:
[icexmoon@xyz ~]$ chfn
Changing finger information for icexmoon.
名称 [icexmoon]: 魔芋红茶
办公 []:
办公电话 []: 123456
住宅电话 []: 123456
密码:
Finger information changed.
[icexmoon@xyz ~]$ finger icexmoon
Login: icexmoon Name: 魔芋红茶
Directory: /home/icexmoon Shell: /bin/bash
Office: 123456, 123456
On since 五 8月 20 18:16 (CST) on pts/0 from icexmoon-book
5 seconds idle
No mail.
Plan:
This is a test plan.
有点像是个人主页上的那些信息,但一般来说,除非你想在Linux主机上搞个账户社区啥的,这个命令用处不大。
chsh
命令用于修改用户的登录用Shell。
先列出当前Linux上可用的shell:
[chagetest@xyz ~]$ chsh -l
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
/bin/tcsh
/bin/csh
修改自己的登录用shell:
[chagetest@xyz ~]$ chsh -s /bin/csh
Changing shell for chagetest.
密码:
Shell changed.
[chagetest@xyz ~]$ cat /etc/passwd | grep chagetest
chagetest:x:1501:1501::/home/chagetest:/bin/csh
[chagetest@xyz ~]$ chsh -s /bin/bash
Changing shell for chagetest.
密码:
Shell changed.
[chagetest@xyz ~]$ cat /etc/passwd | grep chagetest
chagetest:x:1501:1501::/home/chagetest:/bin/bash
使用groupadd
可以创建新的用户组:
[root@xyz ~]# groupadd group1
[root@xyz ~]# grep group1 /etc/group /etc/gshadow
/etc/group:group1:x:1502:
/etc/gshadow:group1:!::
默认情况下会分配一个大于1000的GID,这样会造成你之后创建的新用户分配的UID和GID不再是相同数字的值,所以也有教程会建议自行创建用户组时使用小于1000的GID,但事实上保持UID和GID相等并没有什么实际意义。
使用groupmod
可以修改已有用户组的信息:
[root@xyz ~]# groupmod -g 201 -n mygroup group1
[root@xyz ~]# grep mygroup /etc/group /etc/gshadow
/etc/group:mygroup:x:201:
/etc/gshadow:mygroup:!::
修改用户组的GID需要慎重,因为文件权限等都是与GID直接相关的。
使用groupdel
可以删除用户组:
[root@xyz ~]# groupdel mygroup
[root@xyz ~]# groupdel user1
groupdel:不能移除用户“user1”的主组
需要注意的是,如果一个用户组是依然存在的某个用户的主用户组(初始用户组),则是不能被删除的。这很好理解,如果删除了,那个用户就没有主用户组了,这显然是不可接受的。
如果依然要删除,可以先给该用户指定一个新的主用户组,或者删除该用户,然后再删除这个用户组。
gpasswd
这个命令就是之前将的设置用户管理员的相关命令:
[root@xyz ~]# groupadd grouptest
[root@xyz ~]# gpasswd grouptest
正在修改 grouptest 组的密码
新密码:
请重新输入新密码:
[root@xyz ~]# gpasswd -A user1 grouptest
这里新建了一个用户组grouptest
,并将user1
用户设置为这个用户组的管理员。
现在使用user1
登录系统,并添加用户到该用户组:
❯ ssh [email protected]
[email protected]'s password:
Last failed login: Sat Aug 21 10:20:30 CST 2021 from icexmoon-book on ssh:notty
There was 1 failed login attempt since the last successful login.
[user1@xyz ~]$ id
uid=1001(user1) gid=1001(user1) 组=1001(user1) 环境=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[user1@xyz ~]$ gpasswd -a user1 grouptest
正在将用户“user1”加入到“grouptest”组中
[user1@xyz ~]$ gpasswd -a user3 grouptest
正在将用户“user3”加入到“grouptest”组中
[user1@xyz ~]$ grep grouptest /etc/group
grouptest:x:1502:user1,user3
[user1@xyz ~]$ id
uid=1001(user1) gid=1001(user1) 组=1001(user1) 环境=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
这里需要注意的是:
先创建3个用户,并且给前两个用户添加辅助用户组,将第三个用户设置为不能登录:
[root@xyz ~]# groupadd mygroup1
[root@xyz ~]# useradd -G mygroup1 -c '1st user' myuser1
[root@xyz ~]# useradd -G mygroup1 -c '2nd user' myuser2
[root@xyz ~]# useradd -c '3rd user' -s '/sbin/nologin' myuser3
[root@xyz ~]# echo 'password' | passwd --stdin myuser1
更改用户 myuser1 的密码 。
passwd:所有的身份验证令牌已经成功更新。
[root@xyz ~]# echo 'password' | passwd --stdin myuser2
更改用户 myuser2 的密码 。
passwd:所有的身份验证令牌已经成功更新。
[root@xyz ~]# echo 'password' | passwd --stdin myuser3
更改用户 myuser3 的密码 。
passwd:所有的身份验证令牌已经成功更新。
创立一个项目文件夹,并让三个用户都对该文件夹拥有全部的权限:
[root@xyz ~]# groupadd projecta
[root@xyz ~]# mkdir /srv/projecta
[root@xyz ~]# ll -d /srv/projecta/
drwxr-xr-x. 2 root root 6 8月 21 10:36 /srv/projecta/
[root@xyz ~]# chmod 2770 /srv/projecta/
[root@xyz ~]# chgrp projecta /srv/projecta/
[root@xyz ~]# ll -d /srv/projecta/
drwxrws---. 2 root projecta 6 8月 21 10:36 /srv/projecta/
[root@xyz ~]# useradd -G projecta -c 'project user' pro1
[root@xyz ~]# useradd -G projecta -c 'project user' pro2
[root@xyz ~]# useradd -G projecta -c 'project user' pro3
[root@xyz ~]# echo 'password' | passwd --stdin pro1
更改用户 pro1 的密码 。
passwd:所有的身份验证令牌已经成功更新。
[root@xyz ~]# echo 'password' | passwd --stdin pro2
更改用户 pro2 的密码 。
passwd:所有的身份验证令牌已经成功更新。
[root@xyz ~]# echo 'password' | passwd --stdin pro3
更改用户 pro3 的密码 。
passwd:所有的身份验证令牌已经成功更新。
上面讲的都是本地的账号和认证系统,在一些网络构建的系统中,可能会有专门的账号管理和认证服务器,进行登录认证和账号管理,这里不做详细说明。
ACL(access control list)是访问控制列表。
之前在介绍文件系统时候介绍的文件权限控制,仅能针对用户、用户组、其他人这三个维度进行粗略控制,如果我们需要这之外的特殊权限控制,比如某个特定用户对某某目录具有读写权限,但又不想讲该用户变为该目录的拥有者。这时候就需要用到ACL。
简单的说,ACL可以针对单一用户、单一文件或目录进行权限设置。
虽然ACL是Linux内核之外的额外功能,但是新的Linux发行版都已默认对ACL提供了支持,要查看系统是否已经开启了ACL,可以:
[root@xyz ~]# dmesg | grep -i acl
[ 3.644493] systemd[1]: systemd 219 running in system mode. (+PAM +AUDIT +SELINUX +IMA -APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 -SECCOMP +BLKID +ELFUTILS +KMOD +IDN)
[ 7.964164] SGI XFS with ACLs, security attributes, no debug enabled
SGI XFS with ACLs
表明当前系统已经为xfs
文件系统开启了ACL功能。
有两个ACL设置相关的命令:
getfacl
:获取某个文件/目录的ACL设置选项。setfacl
:修改某个文件/目录的ACL设置选项。setfacl
如果要使用ACL给某个用户添加上某个文件的相关权限,可以:
[root@xyz ~]# cd /tmp
[root@xyz tmp]# touch acl_test
[root@xyz tmp]# ll acl_test
-rw-r--r--. 1 root root 0 8月 21 11:29 acl_test
[root@xyz tmp]# setfacl -m u:user1:rx acl_test
[root@xyz tmp]# ll acl_test
-rw-r-xr--+ 1 root root 0 8月 21 11:29 acl_test
[root@xyz tmp]# setfacl -m u::rwx acl_test
[root@xyz tmp]# ll acl_test
-rwxr-xr--+ 1 root root 0 8月 21 11:29 acl_test
其中setfacl -m u::rwx acl_test
这种没有指定用户名的做法,是给文件的拥有者设置相关权限。
可以注意到,设置完ACL权限后,文件的信息栏中最后一位标识变成了+
,这表明该文件设置了ACL权限。
getfacl
现在使用getfacl
命令查看上面设置好的ACL权限:
[root@xyz tmp]# getfacl acl_test
# file: acl_test
# owner: root
# group: root
user::rwx
user:user1:r-x
group::r--
mask::r-x
other::r--
可以清楚地看到,文件拥有者(root)的权限为user::rwx
,而user1
的权限为user:user1:r-x
。此外需要注意mask
这个选项,其作用有点像创建目录和文件时候决定默认权限的umask,但并不完全像,因为这里mask
选项的实际用途是决定实际ACL的最终权限,也就是说虽然上边的设置对于root
的权限是rwx
,但因为mask
是r-x
,所以经过mask
的“遮罩”后,实际权限只能是r-x
。对于user1
来说反而没影响,因为本身的设置就是r-x
。因为上边的原因,ACL的mask
设置通常被设置成rwx
比较容易使用。
上边都是纯理论的讨论,实际上作为文件的拥有者和超级管理员,
root
自然是对该文件拥有全部权限的。
ACL的mask选项上边已经介绍过了,现在展示如何修改mask的值:
[root@xyz tmp]# setfacl -m m:r acl_test
[root@xyz tmp]# getfacl acl_test
# file: acl_test
# owner: root
# group: root
user::rwx
user:user1:r-x #effective:r--
group::r--
mask::r--
other::r--
可以看到使用getfacl
打印出的信息中出现了提示#effective:r--
,这正是在说因为mask
的存在,user1
的实际权限是r--
,设置中添加的x
权限不会真正生效。
现在有一个用户myuser1
:
[root@xyz tmp]# id myuser1
uid=1502(myuser1) gid=1504(myuser1) 组=1504(myuser1),1503(mygroup1)
还有一个之前创建的项目目录:
[root@xyz tmp]# ll -d /srv/projecta/
drwxrws---+ 2 root projecta 6 8月 21 10:36 /srv/projecta/
因为myuser1
不属于projecta
,所以自然是无法进入该目录:
[myuser1@xyz ~]$ cd /srv
[myuser1@xyz srv]$ cd projecta/
-bash: cd: projecta/: 权限不够
如果要在不调整从属用户组和目录权限的情况下让myuser1
能进入目录,可以使用ACL权限设置的方式实现:
[root@xyz tmp]# setfacl -m u:myuser1:rx /srv/projecta/
[root@xyz tmp]# getfacl /srv/projecta/
getfacl: Removing leading '/' from absolute path names
# file: srv/projecta/
# owner: root
# group: projecta
# flags: -s-
user::rwx
user:myuser1:r-x
group::rwx
mask::rwx
other::---
使用myuser1
用户进入目录:
[myuser1@xyz srv]$ cd projecta/
[myuser1@xyz projecta]$
默认情况下所设置的ACL权限是不会继承的:
❯ ssh [email protected]
[email protected]'s password:
[pro1@xyz ~]$ cd /srv/projecta/
[pro1@xyz projecta]$ touch test_file
[pro1@xyz projecta]$ mkdir test_dir
[pro1@xyz projecta]$ ls -al
总用量 0
drwxrws---+ 3 root projecta 39 8月 21 11:59 .
drwxr-xr-x. 4 root root 93 8月 21 10:36 ..
drwxrwsr-x. 2 pro1 projecta 6 8月 21 11:59 test_dir
-rw-rw-r--. 1 pro1 projecta 0 8月 21 11:59 test_file
可以看到虽然/srv/projecta
目录设置了ACL权限,但是在其中创建的文件和目录并不会继承ACL权限(权限信息的最后并没有+
)。
如果想让子目录或者文件继承目录的ACL权限,可以:
[root@xyz tmp]# setfacl -m d:u:myuser1:rx /srv/projecta/
[root@xyz tmp]# getfacl /srv/projecta/
getfacl: Removing leading '/' from absolute path names
# file: srv/projecta/
# owner: root
# group: projecta
# flags: -s-
user::rwx
user:myuser1:r-x
group::rwx
mask::rwx
other::---
default:user::rwx
default:user:myuser1:r-x
default:group::rwx
default:mask::rwx
default:other::---
getfacl
打印出的信息中出现了default
的相关内容,那些就是子目录和文件会继承的ACL权限。
重新创建子文件和目录:
[pro1@xyz projecta]$ rmdir test_dir
[pro1@xyz projecta]$ rm -f test_file
[pro1@xyz projecta]$ touch test_file
[pro1@xyz projecta]$ mkdir test_dir
[pro1@xyz projecta]$ ls -al
总用量 0
drwxrws---+ 3 root projecta 39 8月 21 12:04 .
drwxr-xr-x. 4 root root 93 8月 21 10:36 ..
drwxrws---+ 2 pro1 projecta 6 8月 21 12:04 test_dir
-rw-rw----+ 1 pro1 projecta 0 8月 21 12:04 test_file
看到+
了,新的子文件和目录有继承ACL权限。
实际测试一下:
❯ ssh [email protected]
[email protected]'s password:
Last login: Sat Aug 21 11:48:42 2021 from icexmoon-book
[myuser1@xyz ~]$ cd /srv/projecta/
[myuser1@xyz projecta]$ cd test_dir
要想删除一个文件或目录的所有的ACL权限:
[root@xyz tmp]# setfacl -b /srv/projecta/
[root@xyz tmp]# ll /srv/projecta/
总用量 0
drwxrws---+ 2 pro1 projecta 6 8月 21 12:04 test_dir
-rw-rw----+ 1 pro1 projecta 0 8月 21 12:04 test_file
[root@xyz tmp]# ll -d /srv/projecta/
drwxrws---. 3 root projecta 39 8月 21 12:04 /srv/projecta/
需要注意的是默认不会影响到子目录或文件的ACL权限,如果有需要,可以使用-R
参数。
如果只想删除个别用户或者ACL继承权限的,可以:
[root@xyz tmp]# setfacl -x u:myuser1 /srv/projecta/
[root@xyz tmp]# setfacl -x d:u:myuser1 /srv/projecta/
[root@xyz tmp]# getfacl /srv/projecta/
getfacl: Removing leading '/' from absolute path names
# file: srv/projecta/
# owner: root
# group: projecta
# flags: -s-
user::rwx
group::rwx
mask::rwx
other::---
default:user::rwx
default:group::rwx
default:mask::rwx
default:other::---
如果想禁止某个用户使用目录或文件,可以:
[root@xyz tmp]# setfacl -m u:pro3:- /srv/projecta/
[root@xyz tmp]# getfacl /srv/projecta/
getfacl: Removing leading '/' from absolute path names
# file: srv/projecta/
# owner: root
# group: projecta
# flags: -s-
user::rwx
user:pro3:---
group::rwx
mask::rwx
other::---
default:user::rwx
default:group::rwx
default:mask::rwx
default:other::---
-
表示不拥有任何权限。
一般用户要想切换到root
用户,以执行一些root
用户才可以执行的操作,可以:
su -
命令sudo
命令使用su
可以切换身份为其他账号,如果不指定账号名,将切换到root
账号:
[pro1@xyz projecta]$ su
密码:
[root@xyz projecta]# id
uid=0(root) gid=0(root) 组=0(root) 环境=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[root@xyz projecta]# env | grep pro1
USER=pro1
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/pro1/.local/bin:/home/pro1/bin
MAIL=/var/spool/mail/pro1
LOGNAME=pro1
XDG_DATA_DIRS=/home/pro1/.local/share/flatpak/exports/share:/var/lib/flatpak/exports/share:/usr/local/share:/usr/share
需要注意的是,通过这种方式切换到root
账户,实际上是使用的是non-login shell
,所以虽然id
命令显式当前用户的确是root
没错,但是与用户相关的环境变量,如USER
、MAIL
等依然是切换之前的信息。
不过万幸的是至少家目录是root
的家目录:
[root@xyz projecta]# cd ~
[root@xyz ~]# pwd
/root
但不管怎么说,这种没有通过login shell
且没有完全加载用户相关环境变量的情况需要避免,所以如果要切换到root
用户,应该使用su -
:
[pro1@xyz projecta]$ su -
密码:
上一次登录:六 8月 21 15:38:44 CST 2021pts/1 上
[root@xyz ~]# env | grep root
USER=root
MAIL=/var/spool/mail/root
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
PWD=/root
HOME=/root
LOGNAME=root
XDG_DATA_DIRS=/root/.local/share/flatpak/exports/share:/var/lib/flatpak/exports/share:/usr/local/share:/usr/share
[root@xyz ~]#
此外,如果仅是想使用root
权限执行一条命令,执行完后回到原来的用户,可以:
[pro1@xyz projecta]$ head -n 5 /etc/shadow
head: 无法打开"/etc/shadow" 读取数据: 权限不够
[pro1@xyz projecta]$ su - -c 'head -n 5 /etc/shadow'
密码:
root:$6$h5eXZCiKw8ucXyB2$PxxRgKneIDs2TWIpg916ugAfGCfqNf/HJHiXjy0PTPtUmY.pR/e2cUMpjRBqbx5Y0AHNJtjxAf3aOUTek3DpO.::0:99999:7:::
bin:*:18353:0:99999:7:::
daemon:*:18353:0:99999:7:::
adm:*:18353:0:99999:7:::
lp:*:18353:0:99999:7:::
这样的使用方式与sudo
命令类似,不同的是这里需要root
的密码,而sudo
命令仅需要当前登录用户的密码。
如果需要切换到非root
账号,可以:
[pro1@xyz projecta]$ su -l icexmoon
密码:
上一次登录:六 8月 21 09:55:47 CST 2021从 icexmoon-bookpts/0 上
[icexmoon@xyz ~]$ id
uid=1000(icexmoon) gid=1000(icexmoon) 组=1000(icexmoon),10(wheel) 环境=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[icexmoon@xyz ~]$ exit
登出
需要注意的是,虽然不使用参数-l
同样可以切换:su -l icexmoon
。但是这种做法就等同于使用su
切换root
用户,此种情况下使用的是non-login shell
,即不会加载用户相关的环境变量,所以是有问题的,应当避免。
同时,对于账号信息中shell
设置为/sbin/nologain
的账号,同样是无法切换的:
[root@xyz tmp]# su -l sshd
最后一次失败的登录:六 8月 21 16:08:39 CST 2021pts/1 上
最有一次成功登录后有 1 次失败的登录尝试。
This account is currently not available.
[root@xyz tmp]# finger sshd
Login: sshd Name: Privilege-separated SSH
Directory: /var/empty/sshd Shell: /sbin/nologin
Last login 六 8月 21 16:09 (CST) on pts/0
No mail.
No Plan.
事实上并非所有人都可以使用sudo
命令,只有符合配置文件/etc/suders
中的设置的账号才可以。
如果你还记得Linux 之旅 1:安装中安装Linux时候勾选的那个将账户添加为管理员的选项,其实就是在通过
/etc/suders
配置文件设置让该账号具有使用sudo
命令的权限。
使用sudo
命令可以很方便地通过其他用户地身份执行某条命令:
[root@xyz tmp]# sudo -u sshd touch /tmp/sudo_test_file
[root@xyz tmp]# ll /tmp/sudo_test_file
-rw-r--r--. 1 sshd sshd 0 8月 21 16:22 /tmp/sudo_test_file
通过-u
可以指定执行命令的用户,如果不加则为root
用户。
甚至可以执行多条命令:
[root@xyz tmp]# sudo -u sshd sh -c 'mkdir /tmp/sudo_test_dir;cd /tmp/sudo_test_dir;touch sudo_test_file2'
[root@xyz tmp]# ll /tmp/sudo_test_dir
总用量 0
-rw-r--r--. 1 sshd sshd 0 8月 21 16:26 sudo_test_file2
这实际上是借助sh -c
来实现的,有点套娃的意思。
事实上sudo
命令的调用过程如下:
/etc/sudoers
配置文件,并判断当前用户有没有执行sudo
命令的权限。root
不需要输入密码)。sudo
命令后的Shell
命令。之所以会要求用户输入密码,是考虑到用户有可能临时离开电脑去上厕所或喝咖啡,所以要求身份验证。通过后短期内再次使用
sudo
不会再重复要求验证。
上面已经说了,用户能不能使用sudo
是由/etc/sudoers
这个配置文件决定的,如果一个未添加权限的用户使用sudo
命令:
[pro1@xyz projecta]$ sudo head -n 5 /etc/shadow
我们信任您已经从系统管理员那里了解了日常注意事项。
总结起来无外乎这三点:
#1) 尊重别人的隐私。
#2) 输入前要先考虑(后果和风险)。
#3) 权力越大,责任越大。
[sudo] pro1 的密码:
pro1 不在 sudoers 文件中。此事将被报告。
如果要给一个用户开放sudo
权限,自然需要root
来修改这个文件:
[root@xyz tmp]# visudo
这个命令会使用vi
打开/etc/sudoers
,可以搜索root
,然后依葫芦画瓢地设置一个用户可以使用sudo
:
这里这个字段的含义为:
现在使用user1
用户执行sudo
:
[user1@xyz ~]$ sudo head -n 5 /etc/shadow
我们信任您已经从系统管理员那里了解了日常注意事项。
总结起来无外乎这三点:
#1) 尊重别人的隐私。
#2) 输入前要先考虑(后果和风险)。
#3) 权力越大,责任越大。
[sudo] user1 的密码:
root:$6$h5eXZCiKw8ucXyB2$PxxRgKneIDs2TWIpg916ugAfGCfqNf/HJHiXjy0PTPtUmY.pR/e2cUMpjRBqbx5Y0AHNJtjxAf3aOUTek3DpO.::0:99999:7:::
bin:*:18353:0:99999:7:::
daemon:*:18353:0:99999:7:::
adm:*:18353:0:99999:7:::
lp:*:18353:0:99999:7:::
除了直接将用户加入/etc/sudoers
外,还可以通过配置用户组来赋予sudo
使用权限:
[root@xyz tmp]# cat /etc/sudoers | grep -v '^#.*' | grep -v '^$'
Defaults !visiblepw
Defaults always_set_home
Defaults match_group_by_gid
Defaults always_query_group_plugin
Defaults env_reset
Defaults env_keep = "COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS"
Defaults env_keep += "MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE"
Defaults env_keep += "LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES"
Defaults env_keep += "LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE"
Defaults env_keep += "LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY"
Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin
root ALL=(ALL) ALL
user1 ALL=(ALL) ALL
%wheel ALL=(ALL) ALL
看到%wheel ALL=(ALL) ALL
没,这行的意思是用户组wheel
具有使用sudo
的权限,因此我们可以通过将用户添加到wheel
用户组的方式赋予sudo
权限:
[root@xyz tmp]# usermod -a -G wheel pro1
[root@xyz tmp]# id pro1
uid=1505(pro1) gid=1508(pro1) 组=1508(pro1),10(wheel),1507(projecta)
之后登录pro1
测试的过程不再赘述。
如果想每次使用sudo
不再需要输入密码进行验证,可以通过visudo
命令修改配置文件中的如下信息:
将上面红框位置内的信息去掉注释,变成如上的内容后保存即可。
这样做了以后,wheel
用户组内的用户也可以像root
一样在使用sudo
的时候不需要输入密码了。
此外,还可以实现一些更复杂的设置,比如说让某些用户只能通过sudo
执行特定的命令:
可以修改为上边的设置,现在user1
仅能执行passwd
这个命令了(需要注意的是这里指定的命令必须是完整路径):
[user1@xyz ~]$ sudo head -n 5 /etc/shadow
[sudo] user1 的密码:
对不起,用户 user1 无权以 root 的身份在 xyz.icexmoon.centos 上执行 /bin/head -n 5 /etc/shadow。
[user1@xyz ~]$ sudo passwd myuser1
[sudo] user1 的密码:
更改用户 myuser1 的密码 。
新的 密码:
无效的密码: 密码少于 8 个字符
重新输入新的 密码:
passwd:所有的身份验证令牌已经成功更新。
但是要知道的是,user1
用户拥有的是执行passwd
这个命令的全部功能,也就是说也可以通过passwd
命令修改root
用户的密码,一般来说我们显然不允许这样的操作,如果要进一步限制,不允许user1
修改root
的密码,可以:
需要注意这里的命令顺序,!
表示会被阻止的命令形式,如果!/usr/bin/passwd root
放在usr/bin/passwd [a-zA-Z]*
之前就会出错,原因是先阻止再被允许,显然是不正确的规则,所以最好将阻止规则放在允许规则的后边。
现在看效果:
[user1@xyz ~]$ sudo passwd root
对不起,用户 user1 无权以 root 的身份在 xyz.icexmoon.centos 上执行 /bin/passwd root。
[user1@xyz ~]$ sudo passwd
对不起,用户 user1 无权以 root 的身份在 xyz.icexmoon.centos 上执行 /bin/passwd。
[user1@xyz ~]$ sudo passwd myuser1
更改用户 myuser1 的密码 。
新的 密码:
无效的密码: 密码未通过字典检查 - 它基于字典单词
重新输入新的 密码:
passwd:所有的身份验证令牌已经成功更新。
正是我们希望的。
此外还可以在/etc/sudoers
中使用别名:
User_Alias PASSWORD_ADMINS = pro1,pro2,pro3,myuser1,myuser2
Cmnd_Alias PASSWORD_COMMAND = !/usr/bin/passwd,/usr/bin/passwd [a-zA-Z]*,!/usr/bin/passwd root
PASSWORD_ADMINS ALL=(root) PASSWORD_COMMAND
写入上面的信息后保存后,PASSWORD_ADMINS
中关联的用户将可以使用sudo
命令修改一般用户的密码。使用别名可以更容易地配置sudo
命令的管理权限。
最后,在介绍一种用途:借助sudo
命令我们可以实现在不知道root账号密码的情况下切换为root用户,这样可以更好地保护root密码(虽然好像意义并不是特别大,毕竟有sudo
权限的用户都可以修改root
的密码)。
实现的方法也很简单,只要在/etc/sudoers
中添加这样的信息:
pro1 ALL=(root) /usr/bin/su -
然后使用对应的用户这样:
[pro1@xyz ~]$ sudo su -
如果需要输入密码就输入自己账户的,就可以切换为root
了。
事实上《鸟哥的私房菜》这部分章节还有相当一部分内容,但本文的幅度已经很长了,而且我的Typora编辑器已经很卡了,就分为两个章节发吧。
先到这里了,谢谢阅读。