从第五章开始,我们就一直强调 Linux 的权限概念是非常重要的! 但是传统的权限仅有三种身份 ( owner, group, others) 搭配三种权限 ( r,w,x) 而已,并没有办法单纯的针对某一个使用者或某一个群组来设置特定的权限需求,例如前一小节最后的那个任务! 此时就得要使用 ACL 这个机制啦!这玩意挺有趣的,下面我们就来谈一谈:
ACL 是 Access Control List 的缩写,主要的目的是在提供传统的 owner,group,others 的read,write,execute 权限之外的细部权限设置。ACL 可以针对单一使用者,单一文件或目录来进行 r,w,x 的权限规范,对于需要特殊权限的使用状况非常有帮助。
那 ACL 主要可以针对哪些方面来控制权限呢?他主要可以针对几个项目:
使用者 ( user) :可以针对使用者来设置权限;
群组 ( group) :针对群组为对象来设置其权限;
默认属性 ( mask) :还可以针对在该目录下在创建新文件/目录时,规范新数据的默认权限;
也就是说,如果你有一个目录,需要给一堆人使用,每个人或每个群组所需要的权限并不相同时,在过去,传统的 Linux 三种身份的三种权限是无法达到的, 因为基本上,传统的 Linux权限只能针对一个用户、一个群组及非此群组的其他人设置权限而已,无法针对单一用户或个人来设计权限。 而 ACL 就是为了要改变这个问题啊!好了,稍微了解之后,再来看看如何让你的文件系统可以支持 ACL 吧!
事实上,原本 ACL 是 unix-like 操作系统的额外支持项目,但因为近年以来 Linux 系统对权限细部设置的热切需求, 因此目前 ACL 几乎已经默认加入在所有常见的 Linux 文件系统的挂载参数中 ( ext2/ext3/ext4/xfs等等) !所以你无须进行任何动作, ACL 就可以被你使用啰!不过,如果你不放心系统是否真的有支持 ACL 的话,那么就来检查一下核心挂载时显示的信息吧!
[root@study ~]# dmesg | grep -i acl
[ 0.330377] systemd[1]: systemd 208 running in system mode. ( +PAM +LIBWRAP +AUDIT
+SELINUX +IMA +SYSVINIT +LIBCRYPTSETUP +GCRYPT +ACL +XZ)
[ 0.878265] SGI XFS with ACLs, security attributes, large block/inode numbers, no
debug enabled
瞧!至少 xfs 已经支持这个 ACL 的功能啰!
好了,既然知道我们的 filesystem 有支持 ACL 之后,接下来该如何设置与观察 ACL 呢? 很简单,利用这两个指令就可以了:
getfacl:取得某个文件/目录的 ACL 设置项目;
setfacl:设置某个目录/文件的 ACL 规范。
先让我们来瞧一瞧 setfacl 如何使用吧!
setfacl 指令用法介绍及最简单的“ u:帐号:权限 ”设置
[root@study ~]# setfacl [-bkRd] [{-m|-x} acl参数] 目标文件名
选项与参数:
-m :设置后续的 acl 参数给文件使用,不可与 -x 合用;
-x :删除后续的 acl 参数,不可与 -m 合用;
-b :移除“所有的” ACL 设置参数;
-k :移除“默认的” ACL 参数,关于所谓的“默认”参数于后续范例中介绍;
-R :递回设置 acl ,亦即包括次目录都会被设置起来;
-d :设置“默认 acl 参数”的意思!只对目录有效,在该目录新建的数据会引用此默认值
上面谈到的是 acl 的选项功能,那么如何设置 ACL 的特殊权限呢?特殊权限的设置方法有很多, 我们先来谈谈最常见的,就是针对单一使用者的设置方式:
#1. 针对特定使用者的方式:
#设置规范:“ u:[使用者帐号列表]:[rwx] ”,例如针对 vbird1 的权限规范 rx :
[root@study ~]# touch acl_test1
[root@study ~]# ll acl_test1
-rw-r–r--. 1 root root 0 Jul 21 17:33 acl_test1
[root@study ~]# setfacl -m u:vbird1:rx acl_test1
[root@study ~]# ll acl_test1
-rw-r-xr–+ 1 root root 0 Jul 21 17:33 acl_test1
#权限部分多了个 + ,且与原本的权限 ( 644) 看起来差异很大!但要如何查阅呢?
[root@study ~]# setfacl -m u::rwx acl_test1
[root@study ~]# ll acl_test1
-rwxr-xr–+ 1 root root 0 Jul 21 17:33 acl_test1
#设置值中的 u 后面无使用者列表,代表设置该文件拥有者,所以上面显示 root 的权限成为 rwx 了!
上述动作为最简单的 ACL 设置,利用“ u:使用者:权限 ”的方式来设置的啦!设置前请加上 -m这个选项。 如果一个文件设置了 ACL 参数后,他的权限部分就会多出一个 + 号了!但是此时你看到的权限与实际权限可能就会有点误差! 那要如何观察呢?就通过 getfacl 吧!
[root@study ~]# getfacl filename
选项与参数:
getfacl 的选项几乎与 setfacl 相同!所以鸟哥这里就免去了选项的说明啊!
# 请列出刚刚我们设置的 acl_test1 的权限内容:
[root@study ~]# getfacl acl_test1
# file: acl_test1 <==说明文档名而已!
# owner: root <==说明此文件的拥有者,亦即 ls -l 看到的第三使用者字段
# group: root <==此文件的所属群组,亦即 ls -l 看到的第四群组字段
user::rwx <==使用者列表栏是空的,代表文件拥有者的权限
user:vbird1:r-x <==针对 vbird1 的权限设置为 rx ,与拥有者并不同!
group::r-- <==针对文件群组的权限设置仅有 r
mask::r-x <==此文件默认的有效权限 ( mask)
other::r-- <==其他人拥有的权限啰!
上面的数据非常容易查阅吧?显示的数据前面加上 # 的,代表这个文件的默认属性,包括文件名、文件拥有者与文件所属群组。 下面出现的 user, group, mask, other 则是属于不同使用者、群组与有效权限( mask) 的设置值。 以上面的结果来看,我们刚刚设置的 vbird1 对于这个文件具有 r 与 x 的权限啦!这样看的懂吗? 如果看的懂的话,接下来让我们在测试其他类型的 setfacl 设置吧!
# 2\. 针对特定群组的方式:
# 设置规范:“ g:[群组列表]:[rwx] ”,例如针对 mygroup1 的权限规范 rx :
[root@study ~]# setfacl -m g:mygroup1:rx acl_test1
[root@study ~]# getfacl acl_test1
# file: acl_test1
# owner: root
# group: root
user::rwx
user:vbird1:r-x
group::r--
group:mygroup1:r-x <==这里就是新增的部分!多了这个群组的权限设置!
mask::r-x
other::r--
基本上,群组与使用者的设置并没有什么太大的差异啦!如上表所示,非常容易了解意义。不过,你应该会觉得奇怪的是, 那个 mask 是什么东西啊?其实他有点像是“有效权限”的意思!他的意义是: 使用者或群组所设置的权限必须要存在于 mask 的权限设置范围内才会生效,此即“有效权限 ( effective permission) ” 我们举个例子来看,如下所示:
# 3\. 针对有效权限 mask 的设置方式:
# 设置规范:“ m:[rwx] ”,例如针对刚刚的文件规范为仅有 r :
[root@study ~]# setfacl -m m:r acl_test1
[root@study ~]# getfacl acl_test1
# file: acl_test1
# owner: root
# group: root
user::rwx
user:vbird1:r-x #effective:r-- <==vbird1+mask均存在者,仅有 r 而已,x 不会生效
group::r--
group:mygroup1:r-x #effective:r--
mask::r--
other::r--
您瞧,vbird1 与 mask 的集合发现仅有 r 存在,因此 vbird1 仅具有 r 的权限而已,并不存在 x权限!这就是 mask 的功能了!我们可以通过使用 mask 来规范最大允许的权限,就能够避免不小心开放某些权限给其他使用者或群组了。 不过,通常鸟哥都是将 mask 设置为 rwx 啦!然后再分别依据不同的使用者/群组去规范她们的权限就是了。
例题:将前一小节任务二中 /srv/projecta 这个目录,让 myuser1 可以进入查阅,但 myuser1不具有修改的权力。答:由于 myuser1 是独立的使用者与群组,因此无法使用传统的 Linux权限设置。此时使用 ACL 的设置如下:
# 1\. 先测试看看,使用 myuser1 能否进入该目录?
[myuser1@study ~]$ cd /srv/projecta
-bash: cd: /srv/projecta: Permission denied <==确实不可进入!
# 2\. 开始用 root 的身份来设置一下该目录的权限吧!
[root@study ~]# setfacl -m u:myuser1:rx /srv/projecta
[root@study ~]# getfacl /srv/projecta
# file: srv/projecta
# owner: root
# group: projecta
# flags: -s
user::rwx
user:myuser1:r-x <==还是要看看有没有设置成功喔!
group::rwx
mask::rwx
other::---
# 3\. 还是得要使用 myuser1 去测试看看结果!
[myuser1@study ~]$ cd /srv/projecta
[myuser1@study projecta]$ ll -a
drwxrws---+ 2 root projecta 4096 Feb 27 11:29 . <==确实可以查询文件名
drwxr-xr-x 4 root root 4096 Feb 27 11:29 ..
[myuser1@study projecta]$ touch testing
touch: cannot touch `testing': Permission denied <==确实不可以写入!
请注意,上述的 1, 3 步骤使用 myuser1 的身份,2步骤才是使用 root 去设置的!
上面的设置我们就完成了之前任务二的后续需求喔!这么简单呢!接下来让我们来测试一下,如果我用 root 或者是 pro1 的身份去 /srv/projecta 增加文件或目录时,该文件或目录是否能够具有 ACL 的设置? 意思就是说,ACL 的权限设置是否能够被次目录所“继承?”先试看看:
[root@study ~]# cd /srv/projecta
[root@study ~]# touch abc1
[root@study ~]# mkdir abc2
[root@study ~]# ll -d abc*
-rw-r--r--. 1 root projecta 0 Jul 21 17:49 abc1
drwxr-sr-x. 2 root projecta 6 Jul 21 17:49 abc2
你可以明显的发现,权限后面都没有 + ,代表这个 acl 属性并没有继承喔!如果你想要让 acl在目录下面的数据都有继承的功能,那就得如下这样做了!
# 4\. 针对默认权限的设置方式:
# 设置规范:“ d:[ug]:使用者列表:[rwx] ”
# 让 myuser1 在 /srv/projecta 下面一直具有 rx 的默认权限!
[root@study ~]# setfacl -m d:u:myuser1:rx /srv/projecta
[root@study ~]# getfacl /srv/projecta
# 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::---
[root@study ~]# cd /srv/projecta
[root@study projecta]# touch zzz1
[root@study projecta]# mkdir zzz2
[root@study projecta]# ll -d zzz*
-rw-rw----+ 1 root projecta 0 Jul 21 17:50 zzz1
drwxrws---+ 2 root projecta 6 Jul 21 17:51 zzz2
# 看吧!确实有继承喔!然后我们使用 getfacl 再次确认看看!
[root@study projecta]# getfacl zzz2
# file: zzz2
# 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::---
通过这个“针对目录来设置的默认 ACL 权限设置值”的项目,我们可以让这些属性继承到次目录下面呢! 非常方便啊!那如果想要让 ACL 的属性全部消失又要如何处理?通过“ setfacl -b文件名 ”即可啦! 太简单了!鸟哥就不另外介绍了!请自行测试测试吧!
问:针对刚刚的 /srv/projecta 目录的权限设置中,我需要 1) 取消 myuser1 的设置( 连同默认值) ,以及 2) 我不能让 pro3 这个用户使用该目录,亦即 pro3 在该目录下无任何权限,该如何设置?答:取消全部的 ACL 设置可以使用 -b 来处理,但单一设置值的取消,就得要通过 -x 才行了!所以你应该这样作:
# 1.1 找到针对 myuser1 的设置值
[root@study ~]# getfacl /srv/projecta | grep myuser1
user:myuser1:r-x
default:user:myuser1:r-x
# 1.2 针对每个设置值来处理,注意,取消某个帐号的 ACL 时,不需要加上权限项目!
[root@study ~]# setfacl -x u:myuser1 /srv/projecta
[root@study ~]# setfacl -x d:u:myuser1 /srv/projecta
# 2.1 开始让 pro3 这个用户无法使用该目录啰!
[root@study ~]# setfacl -m u:pro3:- /srv/projecta
只需要留意,当设置一个用户/群组没有任何权限的 ACL 语法中,在权限的字段不可留白,而是应该加上一个减号 ( -) 才是正确的作法!
什么?在 Linux 系统当中还要作身份的变换?这是为啥?可能有下面几个原因啦!
事实上,为了安全的缘故,一些老人家都会建议你,尽量以一般身份使用者来操作 Linux 的日常作业!等到需要设置系统环境时, 才变换身份成为 root 来进行系统管理,相对比较安全啦!避免作错一些严重的指令,例如恐怖的“ rm -rf / ”( 千万作不得!)
相对于系统安全,有的时候,我们必须要以某些系统帐号来进行程序的执行。 举例来说,Linux 主机上面的一套软件,名称为 apache ,我们可以额外创建一个名为 apache 的使用者来启动 apache 软件啊,如此一来,如果这个程序被攻破,至少系统还不至于就损毁了~
在远古时代的 telnet 程序中,该程序默认是不许使用 root 的身份登陆的,telnet 会判断登陆者的 UID, 若 UID 为 0 的话,那就直接拒绝登陆了。所以,你只能使用一般使用者来登陆Linux 服务器。 此外, ssh [3] 也可以设置拒绝 root 登陆喔!那如果你有系统设置需求该如何是好啊?就变换身份啊!
由于上述考虑,所以我们都是使用一般帐号登陆系统的,等有需要进行系统维护或软件更新时才转为 root 的身份来动作。 那如何让一般使用者转变身份成为 root 呢?主要有两种方式喔:
以“ su - ”直接将身份变成 root 即可,但是这个指令却需要 root 的密码,也就是说,如果你要以 su 变成 root 的话,你的一般使用者就必须要有 root 的密码才行;
以“ sudo 指令 ”执行 root 的指令串,由于 sudo 需要事先设置妥当,且 sudo 需要输入使用者自己的密码, 因此多人共管同一部主机时, sudo 要比 su 来的好喔!至少 root 密码不会流出去!
下面我们就来说一说 su 跟 sudo 的用法啦!
su 是最简单的身份切换指令了,他可以进行任何身份的切换唷!方法如下:
[root@study ~]# su [-lm] [-c 指令] [username]
选项与参数:
上表的解释当中有出现之前第十章谈过的 login-shell 配置文件读取方式,如果你忘记那是啥东西, 请先回去第十章瞧瞧再回来吧!这个 su 的用法当中,有没有加上那个减号“ - ”差很多喔! 因为涉及 login-shell 与 non-login shell 的变量读取方法。这里让我们以一个小例子来说明吧!
范例一:假设你原本是 dmtsai 的身份,想要使用 non-login shell 的方式变成 root
[dmtsai@study ~]$ su <==注意提示字符,是 dmtsai 的身份喔!
Password: <==这里输入 root 的密码喔!
[root@study dmtsai]# id <==提示字符的目录是 dmtsai 喔!
uid=0( root) gid=0( root) groups=0( root) context=unconf.... <==确实是 root 的身份!
[root@study dmtsai]# env | grep 'dmtsai'
USER=dmtsai <==竟然还是 dmtsai 这家伙!
PATH=...:/home/dmtsai/.local/bin:/home/dmtsai/bin <==这个影响最大!
MAIL=/var/spool/mail/dmtsai <==收到的 mailbox 是 vbird1
PWD=/home/dmtsai <==并非 root 的主文件夹
LOGNAME=dmtsai
# 虽然你的 UID 已经是具有 root 的身份,但是看到上面的输出讯息吗?
# 还是有一堆变量为原本 dmtsai 的身份,所以很多数据还是无法直接利用。
[root@study dmtsai]# exit <==这样可以离开 su 的环境!
单纯使用“ su ”切换成为 root 的身份,读取的变量设置方式为 non-login shell 的方式,这种方式很多原本的变量不会被改变, 尤其是我们之前谈过很多次的 PATH 这个变量,由于没有改变成为 root 的环境, 因此很多 root 惯用的指令就只能使用绝对路径来执行咯。其他的还有MAIL 这个变量,你输入 mail 时, 收到的邮件竟然还是 dmtsai 的,而不是 root 本身的邮件!是否觉得很奇怪啊!所以切换身份时,请务必使用如下的范例二:
范例二:使用 login shell 的方式切换为 root 的身份并观察变量
[dmtsai@study ~]$ su -
Password: <==这里输入 root 的密码喔!
[root@study ~]# 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
# 了解差异了吧?下次变换成为 root 时,记得最好使用 su - 喔!
[root@study ~]# exit <==这样可以离开 su 的环境!
上述的作法是让使用者的身份变成 root 并开始操作系统,如果想要离开 root 的身份则得要利用 exit 离开才行。 那我如果只是想要执行“一个只有 root 才能进行的指令,且执行完毕就恢复原本的身份”呢?那就可以加上 -c 这个选项啰! 请参考下面范例三!
范例三:dmtsai 想要执行“ head -n 3 /etc/shadow ”一次,且已知 root 密码
[dmtsai@study ~]$ head -n 3 /etc/shadow
head: cannot open `/etc/shadow' for reading: Permission denied
[dmtsai@study ~]$ su - -c "head -n 3 /etc/shadow"
Password: <==这里输入 root 的密码喔!
root:$6$wtbCCce/PxMeE5wm$KE2IfSJr.YLP7Rcai6oa/T7KFhOYO62vDnqfLw85...:16559:0:99999:7:::
bin:*:16372:0:99999:7:::
daemon:*:16372:0:99999:7:::
[dmtsai@study ~]$ <==注意看,身份还是 dmtsai 喔!继续使用旧的身份进行系统操作!
由于 /etc/shadow 权限的关系,该文件仅有 root 可以查阅。为了查阅该文件,所以我们必须要使用 root 的身份工作。 但我只想要进行一次该指令而已,此时就使用类似上面的语法吧!好,那接下来,如果我是 root 或者是其他人, 想要变更成为某些特殊帐号,可以使用如下的方法来切换喔!
范例四:原本是 dmtsai 这个使用者,想要变换身份成为 vbird1 时?
[dmtsai@study ~]$ su -l vbird1
Password: <==这里输入 vbird1 的密码喔!
[vbird1@study ~]$ su -
Password: <==这里输入 root 的密码喔!
[root@study ~]# id sshd
uid=74( sshd) gid=74( sshd) groups=74( sshd) ... <==确实有存在此人
[root@study ~]# su -l sshd
This account is currently not available. <==竟然说此人无法切换?
[root@study ~]# finger sshd
Login: sshd Name: Privilege-separated SSH
Directory: /var/empty/sshd Shell: /sbin/nologin
[root@study ~]# exit <==离开第二次的 su
[vbird1@study ~]$ exit <==离开第一次的 su
[dmtsai@study ~]$ exit <==这才是最初的环境!
su 就这样简单的介绍完毕,总结一下他的用法是这样的:
若要完整的切换到新使用者的环境,必须要使用“ su - username ”或“ su -l username ”,才会连同 PATH/USER/MAIL 等变量都转成新使用者的环境;
如果仅想要执行一次 root 的指令,可以利用“ su - -c “指令串” ”的方式来处理;
使用 root 切换成为任何使用者时,并不需要输入新使用者的密码;
虽然使用 su 很方便啦,不过缺点是,当我的主机是多人共管的环境时,如果大家都要使用su 来切换成为 root 的身份,那么不就每个人都得要知道 root 的密码,这样密码太多人知道可能会流出去, 很不妥当呢!怎办?通过 sudo 来处理即可!
相对于 su 需要了解新切换的使用者密码 ( 常常是需要 root 的密码) , sudo 的执行则仅需要自己的密码即可! 甚至可以设置不需要密码即可执行 sudo 呢!由于 sudo 可以让你以其他用户的身份执行指令 ( 通常是使用 root 的身份来执行指令) ,因此并非所有人都能够执行sudo , 而是仅有规范到 /etc/sudoers 内的用户才能够执行 sudo 这个指令喔!说的这么神奇,下面就来瞧瞧那 sudo 如何使用?
Tips 事实上,一般用户能够具有 sudo 的使用权,就是管理员事先审核通过后,才开放 sudo的使用权的!因此,除非是信任用户,否则一般用户默认是不能操作 sudo 的喔!
由于一开始系统默认仅有 root 可以执行 sudo ,因此下面的范例我们先以 root 的身份来执行,等到谈到 visudo 时,再以一般使用者来讨论其他 sudo 的用法吧! sudo 的语法如下:
Tips 还记得在安装 CentOS 7 的第三章时,在设置一般帐号的项目中,有个“让这位使用者成为管理员”的选项吧?如果你有勾选该选项的话, 那除了 root 之外,该一般用户确实是可以使用 sudo 的喔( 以鸟哥的例子来说, dmtsai 默认竟然可以使用 sudo 了!) !这是因为创建帐号的时候,默认将此用户加入 sudo 的支持中了!详情本章稍后告知!
[root@study ~]# sudo [-b] [-u 新使用者帐号]
选项与参数:
-b :将后续的指令放到背景中让系统自行执行,而不与目前的 shell 产生影响
-u :后面可以接欲切换的使用者,若无此项则代表切换身份为 root 。
范例一:你想要以 sshd 的身份在 /tmp 下面创建一个名为 mysshd 的文件
[root@study ~]# sudo -u sshd touch /tmp/mysshd
[root@study ~]# ll /tmp/mysshd
-rw-r--r--. 1 sshd sshd 0 Jul 21 23:37 /tmp/mysshd
# 特别留意,这个文件的权限是由 sshd 所创建的情况喔!
范例二:你想要以 vbird1 的身份创建 ~vbird1/www 并于其中创建 index.html 文件
[root@study ~]# sudo -u vbird1 sh -c <u>"mkdir ~vbird1/www; cd ~vbird1/www; \</u>
> <u>echo 'This is index.html file' > index.html"</u>
[root@study ~]# ll -a ~vbird1/www
drwxr-xr-x. 2 vbird1 vbird1 23 Jul 21 23:38 .
drwx------. 6 vbird1 vbird1 4096 Jul 21 23:38 ..
-rw-r--r--. 1 vbird1 vbird1 24 Jul 21 23:38 index.html
# 要注意,创建者的身份是 vbird1 ,且我们使用 sh -c "一串指令" 来执行的
sudo 可以让你切换身份来进行某项任务,例如上面的两个范例。范例一中,我们的 root 使用sshd 的权限去进行某项任务! 要注意,因为我们无法使用“ su - sshd ”去切换系统帐号 (因为系统帐号的 shell 是 /sbin/nologin), 这个时候 sudo 真是他 X 的好用了!立刻以 sshd 的权限在 /tmp 下面创建文件!查阅一下文件权限你就了解意义啦! 至于范例二则更使用多重指令串 (通过分号 ; 来延续指令进行),使用 sh -c 的方法来执行一连串的指令, 如此真是好方便!
但是 sudo 默认仅有 root 能使用啊!为什么呢?因为 sudo 的执行是这样的流程:
所以说,sudo 执行的重点是:“能否使用 sudo 必须要看 /etc/sudoers 的设置值, 而可使用sudo 者是通过输入使用者自己的密码来执行后续的指令串”喔!由于能否使用与 /etc/sudoers有关, 所以我们当然要去编辑 sudoers 文件啦!不过,因为该文件的内容是有一定的规范的,因此直接使用 vi 去编辑是不好的。 此时,我们得要通过 visudo 去修改这个文件喔!
从上面的说明我们可以知道,除了 root 之外的其他帐号,若想要使用 sudo 执行属于 root 的权限指令,则 root 需要先使用 visudo 去修改 /etc/sudoers ,让该帐号能够使用全部或部分的root 指令功能。为什么要使用 visudo 呢?这是因为 /etc/sudoers 是有设置语法的,如果设置错误那会造成无法使用 sudo 指令的不良后果。因此才会使用 visudo 去修改, 并在结束离开修改画面时,系统会去检验 /etc/sudoers 的语法就是了。
一般来说,visudo 的设置方式有几种简单的方法喔,下面我们以几个简单的例子来分别说明:
假如我们要让 vbird1 这个帐号可以使用 root 的任何指令,基本上有两种作法,第一种是直接通过修改 /etc/sudoers ,方法如下:
[root@study ~]# visudo
....(前面省略)....
root ALL=(ALL) ALL <==找到这一行,大约在 98 行左右
vbird1 ALL=(ALL) ALL <==这一行是你要新增的!
....(下面省略)
有趣吧!其实 visudo 只是利用 vi 将 /etc/sudoers 文件调用出来进行修改而已,所以这个文件就是 /etc/sudoers 啦! 这个文件的设置其实很简单,如上面所示,如果你找到 98 行 (有root 设置的那行) 左右,看到的数据就是:
使用者帐号 登陆者的来源主机名称=(可切换的身份) 可下达的指令
root ALL=(ALL) ALL <==这是默认值
上面这一行的四个元件意义是:
[vbird1@study ~]$ tail -n 1 /etc/shadow <==注意!身份是 vbird1
tail: cannot open `/etc/shadow' for reading: Permission denied
# 因为不是 root 嘛!所以当然不能查询 /etc/shadow
[vbird1@study ~]$ sudo tail -n 1 /etc/shadow <==通过 sudo
We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:
#1) Respect the privacy of others. <==这里仅是一些说明与警示项目
#2) Think before you type.
#3) With great power comes great responsibility.
[sudo] password for vbird1: <==注意啊!这里输入的是“ vbird1 自己的密码 ”
pro3:$6$DMilzaKr$OeHeTDQPHzDOz/u5Cyhq1Q1dy...:16636:0:99999:7:::
# 看!vbird1 竟然可以查询 shadow !
注意到了吧!vbird1 输入自己的密码就能够执行 root 的指令!所以,系统管理员当然要了解vbird1 这个用户的“操守”才行!否则随便设置一个使用者,他恶搞系统怎办?另外,一个一个设置太麻烦了, 能不能使用群组的方式来设置呢?参考下面的第二种方式吧。
我们在本章前面曾经创建过 pro1, pro2, pro3 ,这三个用户能否通过群组的功能让这三个人可以管理系统? 可以的,而且很简单!同样我们使用实际案例来说明:
[root@study ~]# visudo <==同样的,请使用 root 先设置
....(前面省略)....
%wheel ALL=(ALL) ALL <==大约在 106 行左右,请将这行的 # 拿掉!
# 在最左边加上 % ,代表后面接的是一个“群组”之意!改完请储存后离开
[root@study ~]# usermod -a -G wheel pro1 <==将 pro1 加入 wheel 的支持
上面的设置值会造成“任何加入 wheel 这个群组的使用者,就能够使用 sudo 切换任何身份来操作任何指令”的意思。 你当然可以将 wheel 换成你自己想要的群组名。接下来,请分别切换身份成为 pro1 及 pro2 试看看 sudo 的运行。
[pro1@study ~]$ sudo tail -n 1 /etc/shadow <==注意身份是 pro1
....(前面省略)....
[sudo] password for pro1: <==输入 pro1 的密码喔!
pro3:$6$DMilzaKr$OeHeTDQPHzDOz/u5Cyhq1Q1dy...:16636:0:99999:7:::
[pro2@study ~]$ sudo tail -n 1 /etc/shadow <==注意身份是 pro2
[sudo] password for pro2: <==输入 pro2 的密码喔!
pro2 is not in the sudoers file. This incident will be reported.
# 仔细看错误讯息他是说这个 pro2 不在 /etc/sudoers 的设置中!
这样理解群组了吧?如果你想要让 pro3 也支持这个 sudo 的话,不需要重新使用 visudo ,只要利用 usermod 去修改 pro3 的群组支持,让 pro3 用户加入 wheel 群组当中,那他就能够进行 sudo 啰! 好了!那么现在你知道为啥在安装时创建的用户,就是那个 dmstai 默认可以使用 sudo 了吗?请使用“ id dmtsai ”看看, 这个用户是否有加入 wheel 群组呢?嘿嘿!了解乎?
Tips 从 CentOS 7 开始,在 sudoers 文件中,默认已经开放 %wheel 那一行啰!以前的CentOS 旧版本都是没有启用的呢!
简单吧!不过,既然我们都信任这些 sudo 的用户了,能否提供“不需要密码即可使用 sudo”呢? 就通过如下的方式:
[root@study ~]# visudo <==同样的,请使用 root 先设置
....(前面省略)....
%wheel ALL=(ALL) NOPASSWD: ALL <==大约在 109 行左右,请将 # 拿掉!
# 在最左边加上 % ,代表后面接的是一个“群组”之意!改完请储存后离开
重点是那个 NOPASSWD 啦!该关键字是免除密码输入的意思喔!
上面两点都会让使用者能够利用 root 的身份进行任何事情!这样总是不太好~如果我想要让使用者仅能够进行部分系统任务, 比方说,系统上面的 myuser1 仅能够帮 root 修改其他使用者的密码时,亦即“当使用者仅能使用 passwd 这个指令帮忙 root 修改其他用户的密码”时,你该如何撰写呢?可以这样做:
[root@study ~]# visudo <==注意是 root 身份
myuser1 ALL=(root) /usr/bin/passwd <==最后指令务必用绝对路径
上面的设置值指的是“myuser1 可以切换成为 root 使用 passwd 这个指令”的意思。其中要注意的是: 指令字段必须要填写绝对路径才行!否则 visudo 会出现语法错误的状况发生! 此外,上面的设置是有问题的!我们使用下面的指令操作来让您了解:
[myuser1@study ~]$ sudo passwd myuser3 <==注意,身份是 myuser1
[sudo] password for myuser1: <==输入 myuser1 的密码
Changing password for user myuser3\. <==下面改的是 myuser3 的密码喔!这样是正确的
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
[myuser1@study ~]$ sudo passwd
Changing password for user root. <==见鬼!怎么会去改 root 的密码?
恐怖啊!我们竟然让 root 的密码被 myuser1 给改变了!下次 root 回来竟无法登陆系统…欲哭无泪~怎办? 所以我们必须要限制使用者的指令参数!修改的方法为将上述的那行改一改先:
[root@study ~]# visudo <==注意是 root 身份
myuser1 ALL=(root) !/usr/bin/passwd, /usr/bin/passwd [A-Za-z]*, !/usr/bin/passwd root
在设置值中加上惊叹号“ ! ”代表“不可执行”的意思。因此上面这一行会变成:可以执行“passwd 任意字符”,但是“ passwd ”与“ passwd root ”这两个指令例外! 如此一来 myuser1 就无法改变 root 的密码了!这样这位使用者可以具有 root 的能力帮助你修改其他用户的密码,而且也不能随意改变 root 的密码!很有用处的!
如上述第三点,如果我有 15 个用户需要加入刚刚的管理员行列,那么我是否要将上述那长长的设置写入 15 行啊? 而且如果想要修改命令或者是新增命令时,那我每行都需要重新设置,很麻烦ㄟ!有没有更简单的方式? 是有的!通过别名即可!我们 visudo 的别名可以是“指令别名、帐号别名、主机别名”等。不过这里我们仅介绍帐号别名, 其他的设置值有兴趣的话,可以自行玩玩!
假设我的 pro1, pro2, pro3 与 myuser1, myuser2 要加入上述的密码管理员的 sudo 列表中,那我可以创立一个帐号别名称为 ADMPW 的名称,然后将这个名称处理一下即可。处理的方式如下:
[root@study ~]# visudo <==注意是 root 身份
User_Alias ADMPW = pro1, pro2, pro3, myuser1, myuser2
Cmnd_Alias ADMPWCOM = !/usr/bin/passwd, /usr/bin/passwd [A-Za-z]*, !/usr/bin/passwd root
ADMPW ALL=(root) ADMPWCOM
我通过 User_Alias 创建出一个新帐号,这个帐号名称一定要使用大写字符来处理,包Cmnd_Alias(命令别名)、Hos_Alias(来源主机名称别名) 都需要使用大写字符的!这个ADMPW 代表后面接的那些实际帐号。 而该帐号能够进行的指令就如同 ADMPWCOM 后面所指定的那样!上表最后一行则写入这两个别名 (帐号与指令别名), 未来要修改时,我只要修改 User_Alias 以及 Cmnd_Alias 这两行即可!设置方面会比较简单有弹性喔!
或许您已经发现了,那就是,如果我使用同一个帐号在短时间内重复操作 sudo 来运行指令的话, 在第二次执行 sudo 时,并不需要输入自己的密码!sudo 还是会正确的运行喔!为什么呢? 第一次执行 sudo 需要输入密码,是担心由于使用者暂时离开座位,但有人跑来你的座位使用你的帐号操作系统之故。 所以需要你输入一次密码重新确认一次身份。
两次执行 sudo 的间隔在五分钟内,那么再次执行 sudo 时就不需要再次输入密码了, 这是因为系统相信你在五分钟内不会离开你的作业,所以执行 sudo 的是同一个人!呼呼!真是很人性化的设计啊~ _。不过如果两次 sudo 操作的间隔超过 5 分钟,那就得要重新输入一次你的密码了 [4]
很多时候我们需要大量执行很多 root 的工作,所以一直使用 sudo 觉得很烦ㄟ!那有没有办法使用 sudo 搭配 su , 一口气将身份转为 root ,而且还用使用者自己的密码来变成 root呢?是有的!而且方法简单的会让你想笑! 我们创建一个 ADMINS 帐号别名,然后这样做:
[root@study ~]# visudo
User_Alias ADMINS = pro1, pro2, pro3, myuser1
ADMINS ALL=(root) /bin/su -
接下来,上述的 pro1, pro2, pro3, myuser1 这四个人,只要输入“ sudo su - ”并且输入“自己的密码”后, 立刻变成 root 的身份!不但 root 密码不会外流,使用者的管理也变的非常方便!这也是实务上面多人共管一部主机时常常使用的技巧呢!这样管理确实方便,不过还是要强调一下大前提, 那就是“这些你加入的使用者,全部都是你能够信任的用户”!
我们前面一直谈到的大多是一般身份使用者与系统管理员 (root) 的相关操作, 而且大多是讨论关于可登陆系统的帐号来说。那么换个角度想,如果我今天想要创建的, 是一个“仅能使用 mail server 相关邮件服务的帐号,而该帐号并不能登陆 Linux 主机”呢?如果不能给予该帐号一个密码,那么该帐号就无法使用系统的各项资源,当然也包括 mail 的资源, 而如果给予一个密码,那么该帐号就可能可以登陆 Linux 主机啊!呵呵~伤脑筋吧~ 所以,下面让我们来谈一谈这些有趣的话题啰!
另外,在本章之前谈到过 /etc/login.defs 文件中,关于密码长度应该默认是 5 个字串长度,但是我们上面也谈到,该设置值已经被 PAM 模块所取代了,那么 PAM 是什么?为什么他可以影响我们使用者的登陆呢?这里也要来谈谈的!
在本章一开头的 passwd 文件结构里面我们就谈过系统帐号这玩意儿,这玩意儿的 shell 就是使用 /sbin/nologin ,重点在于系统帐号是不需要登陆的!所以我们就给他这个无法登陆的合法 shell。 使用了这个 shell 的用户即使有了密码,你想要登陆时他也无法登陆,因为会出现如下的讯息喔:
This account is currently not available.
我们所谓的“无法登陆”指的仅是:“这个使用者无法使用 bash 或其他 shell 来登陆系统”而已,并不是说这个帐号就无法使用其他的系统资源喔! 举例来说,各个系统帐号,打印工作由 lp这个帐号在管理, WWW 服务由 apache 这个帐号在管理, 他们都可以进行系统程序的工作,但是“就是无法登陆主机取得互动的 shell”而已啦!_
换个角度来想,如果我的 Linux 主机提供的是邮件服务,所以说,在这部 Linux 主机上面的帐号, 其实大部分都是用来收受主机的信件而已,并不需要登陆主机的呢! 这个时候,我们就可以考虑让单纯使用 mail 的帐号以 /sbin/nologin 做为他们的 shell , 这样,最起码当我的主机被尝试想要登陆系统以取得 shell 环境时,可以拒绝该帐号呢!
另外,如果我想要让某个具有 /sbin/nologin 的使用者知道,他们不能登陆主机时, 其实我可以创建“ /etc/nologin.txt ”这个文件, 并且在这个文件内说明不能登陆的原因,那么下次当这个使用者想要登陆系统时, 屏幕上出现的就会是 /etc/nologin.txt 这个文件的内容,而不是默认的内容了!
例题:当使用者尝试利用纯 mail 帐号 (例如 myuser3) 时,利用 /etc/nologin.txt 告知使用者不要利用该帐号登陆系统。答:直接以 vim 编辑该文件,内容可以是这样:
[root@study ~]# vim /etc/nologin.txt
This account is system account or mail account.
Please DO NOT use this account to login my Linux server.
想要测试时,可以使用 myuser3 (此帐号的 shell 是 /sbin/nologin) 来测试看看!
[root@study ~]# su - myuser3
This account is system account or mail account.
Please DO NOT use this account to login my Linux server
结果会发现与原本的默认讯息不一样喔! _
在过去,我们想要对一个使用者进行认证 (authentication),得要要求使用者输入帐号密码, 然后通过自行撰写的程序来判断该帐号密码是否正确。也因为如此,我们常常得使用不同的机制来判断帐号密码, 所以搞的一部主机上面拥有多个各别的认证系统,也造成帐号密码可能不同步的验证问题! 为了解决这个问题因此有了 PAM (Pluggable AuthenticationModules 嵌入式模块) 的机制!
PAM 可以说是一套应用程序接口 (Application Programming Interface, API),他提供了一连串的验证机制,只要使用者将验证阶段的需求告知 PAM 后, PAM 就能够回报使用者验证的结果 (成功或失败)。由于 PAM 仅是一套验证的机制,又可以提供给其他程序所调用引用,因此不论你使用什么程序,都可以使用 PAM 来进行验证,如此一来,就能够让帐号密码或者是其他方式的验证具有一致的结果!也让程序设计师方便处理验证的问题喔! [5]
如上述的图示, PAM 是一个独立的 API 存在,只要任何程序有需求时,可以向 PAM 发出验证要求的通知, PAM 经过一连串的验证后,将验证的结果回报给该程序,然后该程序就能够利用验证的结果来进行可登陆或显示其他无法使用的讯息。 这也就是说,你可以在写程序的时候将 PAM 模块的功能加入,就能够利用 PAM 的验证功能啰。 因此目前很多程序都会利用PAM 喔!所以我们才要来学习他啊!
PAM 用来进行验证的数据称为模块 (Modules),每个 PAM 模块的功能都不太相同。举例来说, 还记得我们在本章使用 passwd 指令时,如果随便输入字典上面找的到的字串,passwd 就会回报错误信息了!这是为什么呢?这就是 PAM 的 pam_cracklib.so 模块的功能!他能够判断该密码是否在字典里面! 并回报给密码修改程序,此时就能够了解你的密码强度了。
所以,当你有任何需要判断是否在字典当中的密码字串时,就可以使用 pam_cracklib.so 这个模块来验证! 并根据验证的回报结果来撰写你的程序呢!这样说,可以理解 PAM 的功能了吧?
PAM 借由一个与程序相同文件名的配置文件来进行一连串的认证分析需求。我们同样以passwd 这个指令的调用 PAM 来说明好了。 当你执行 passwd 后,这支程序调用 PAM 的流程是:
从上头的说明,我们会知道重点其实是 /etc/pam.d/ 里面的配置文件,以及配置文件所调用的PAM 模块进行的验证工作! 既然一直谈到 passwd 这个密码修改指令,那我们就来看看/etc/pam.d/passwd 这个配置文件的内容是怎样吧!
PAM 借由一个与程序相同文件名的配置文件来进行一连串的认证分析需求。我们同样以passwd 这个指令的调用 PAM 来说明好了。 当你执行 passwd 后,这支程序调用 PAM 的流程是:
1. 使用者开始执行 /usr/bin/passwd 这支程序,并输入密码;
2. passwd 调用 PAM 模块进行验证;
3. PAM 模块会到 /etc/pam.d/ 找寻与程序 (passwd) 同名的配置文件;
4. 依据 /etc/pam.d/passwd 内的设置,引用相关的 PAM 模块逐步进行验证分析;
5. 将验证结果 (成功、失败以及其他讯息) 回传给 passwd 这支程序;
6. passwd 这支程序会根据 PAM 回传的结果决定下一个动作 (重新输入新密码或者通过验证!)
从上头的说明,我们会知道重点其实是 /etc/pam.d/ 里面的配置文件,以及配置文件所调用的PAM 模块进行的验证工作! 既然一直谈到 passwd 这个密码修改指令,那我们就来看看/etc/pam.d/passwd 这个配置文件的内容是怎样吧!
在这个配置文件当中,除了第一行宣告 PAM 版本之外,其他任何“ # ”开头的都是注解,而每一行都是一个独立的验证流程, 每一行可以区分为三个字段,分别是验证类别(type)、控制标准(flag)、PAM的模块与该模块的参数。 下面我们先来谈谈验证类别与控制标准这两项数据吧!
Tips 你会发现在我们上面的表格当中出现的是“ include (包括) ”这个关键字,他代表的是“请调用后面的文件来作为这个类别的验证”, 所以,上述的每一行都要重复调用/etc/pam.d/system-auth 那个文件来进行验证的意思!
验证类别主要分为四种,分别说明如下:auth 是 authentication (认证) 的缩写,所以这种类别主要用来检验使用者的身份验证,这种类别通常是需要密码来检验的, 所以后续接的模块是用来检验使用者的身份。
验证类别主要分为四种,分别说明如下:
auth 是 authentication (认证) 的缩写,所以这种类别主要用来检验使用者的身份验证,这种类别通常是需要密码来检验的, 所以后续接的模块是用来检验使用者的身份。
account account (帐号) 则大部分是在进行 authorization (授权),这种类别则主要在检验使用者是否具有正确的使用权限, 举例来说,当你使用一个过期的密码来登陆时,当然就无法正确的登陆了。
session session 是会议期间的意思,所以 session 管理的就是使用者在这次登陆 (或使用这个指令) 期间,PAM 所给予的环境设置。 这个类别通常用在记录使用者登陆与登出时的信息!例如,如果你常常使用 su 或者是 sudo 指令的话, 那么应该可以在/var/log/secure 里面发现很多关于 pam 的说明,而且记载的数据是“session open,session close”的信息!
password password 就是密码嘛!所以这种类别主要在提供验证的修订工作,举例来说,就是修改/变更密码啦!
这四个验证的类型通常是有顺序的,不过也有例外就是了。 会有顺序的原因是,(1)我们总是得要先验证身份 (auth) 后, (2)系统才能够借由使用者的身份给予适当的授权与权限设置 (account),而且(3)登陆与登出期间的环境才需要设置, 也才需要记录登陆与登出的信息 (session)。如果在运行期间需要密码修订时,(4)才给予 password 的类别。这样说起来, 自然是需要有点顺序吧!
那么“验证的控制旗标(control flag)”又是什么?简单的说,他就是“验证通过的标准”啦! 这个字段在管控该验证的放行方式,主要也分为四种控制方式:
required 此验证若成功则带有 success (成功) 的标志,若失败则带有 failure 的标志,但不论成功或失败都会继续后续的验证流程。 由于后续的验证流程可以继续进行,因此相当有利于数据的登录 (log) ,这也是 PAM 最常使用 required 的原因。
requisite 若验证失败则立刻回报原程序 failure 的标志,并终止后续的验证流程。若验证成功则带有 success 的标志并继续后续的验证流程。 这个项目与 required 最大的差异,就在于失败的时候还要不要继续验证下去?由于 requisite 是失败就终止, 因此失败时所产生的 PAM 信息就无法通过后续的模块来记录了。
sufficient 若验证成功则立刻回传 success 给原程序,并终止后续的验证流程;若验证失败则带有 failure 标志并继续后续的验证流程。 这玩意儿与 requisits 刚好相反!
optional 这个模块控制项目大多是在显示讯息而已,并不是用在验证方面的。
如果将这些控制旗标以图示的方式配合成功与否的条件绘图,会有点像下面这样:
程序运行过程中遇到验证时才会去调用 PAM ,而 PAM 验证又分很多类型与控制,不同的控制旗标所回报的讯息并不相同。 如上图所示, requisite 失败就回报了并不会继续,而sufficient 则是成功就回报了也不会继续。 至于验证结束后所回报的信息通常是“succes 或failure ”而已,后续的流程还需要该程序的判断来继续执行才行。
谈完了配置文件的语法后,现在让我们来查阅一下 CentOS 5.x 提供的 PAM 默认文件的内容是啥吧! 由于我们常常需要通过各种方式登陆 (login) 系统,因此就来看看登陆所需要的PAM 流程为何:
[root@study ~]# cat /etc/pam.d/login
#%PAM-1.0
auth [user_unknown=ignore success=ok ignore=ignore default=bad] pam_securetty.so
auth substack system-auth
auth include postlogin
account required pam_nologin.so
account include system-auth
password include system-auth
# pam_selinux.so close should be the first session rule
session required pam_selinux.so close
session required pam_loginuid.so
session optional pam_console.so
# pam_selinux.so open should only be followed by sessions to be executed in the user context
session required pam_selinux.so open
session required pam_namespace.so
session optional pam_keyinit.so force revoke
session include system-auth
session include postlogin
-session optional pam_ck_connector.so
# 我们可以看到,其实 login 也调用多次的 system-auth ,所以下面列出该配置文件
[root@study ~]# cat /etc/pam.d/system-auth
#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth required pam_env.so
auth sufficient pam_fprintd.so
auth sufficient pam_unix.so nullok try_first_pass
auth requisite pam_succeed_if.so uid >= 1000 quiet_success
auth required pam_deny.so
account required pam_unix.so
account sufficient pam_localuser.so
account sufficient pam_succeed_if.so uid < 1000 quiet
account required pam_permit.so
password requisite pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=
password sufficient pam_unix.so sha512 shadow nullok try_first_pass use_authtok
password required pam_deny.so
session optional pam_keyinit.so revoke
session required pam_limits.so
-session optional pam_systemd.so
session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session required pam_unix.so
上面这个表格当中使用到非常多的 PAM 模块,每个模块的功能都不太相同,详细的模块情报可以在你的系统中找到:
/etc/pam.d/:每个程序个别的 PAM 配置文件;
/lib64/security/:PAM 模块文件的实际放置目录;
/etc/security/:其他 PAM 环境的配置文件;
/usr/share/doc/pam-/:详细的 PAM 说明文档。
例如鸟哥使用未 update 过的 CentOS 7.1 ,pamnologin 说明文档在: /usr/share/doc/pam-1.1.8/txts/README.pam_nologin。你可以自行查阅一下该模块的功能。 鸟哥这里仅简单介绍几个较常使用的模块,详细的信息还得要您努力查阅参考书呢! ^^
pam_securetty.so: 限制系统管理员 (root) 只能够从安全的 (secure) 终端机登陆;那什么是终端机?例如 tty1, tty2 等就是传统的终端机设备名称。那么安全的终端机设置呢? 就写在 /etc/securetty 这个文件中。你可以查阅一下该文件, 就知道为什么 root 可以从 tty1~tty7 登陆,但却无法通过 telnet 登陆 Linux 主机了!
pam_nologin.so: 这个模块可以限制一般使用者是否能够登陆主机之用。当 /etc/nologin这个文件存在时,则所有一般使用者均无法再登陆系统了!若 /etc/nologin 存在,则一般使用者在登陆时, 在他们的终端机上会将该文件的内容显示出来!所以,正常的情况下,这个文件应该是不能存在系统中的。 但这个模块对 root 以及已经登陆系统中的一般帐号并没有影响。 (注意喔!这与 /etc/nologin.txt 并不相同!)
pam_selinux.so: SELinux 是个针对程序来进行细部管理权限的功能,SELinux 这玩意儿我们会在第十六章的时候再来详细谈论。由于 SELinux 会影响到使用者执行程序的权限,因此我们利用 PAM 模块,将 SELinux 暂时关闭,等到验证通过后, 再予以启动!
pam_console.so: 当系统出现某些问题,或者是某些时刻你需要使用特殊的终端接口(例如 RS232 之类的终端连线设备) 登陆主机时, 这个模块可以帮助处理一些文件权限的问题,让使用者可以通过特殊终端接口 (console) 顺利的登陆系统。
pam_loginuid.so: 我们知道系统帐号与一般帐号的 UID 是不同的!一般帐号 UID 均大于 1000 才合理。 因此,为了验证使用者的 UID 真的是我们所需要的数值,可以使用这个模块来进行规范!
pam_env.so: 用来设置环境变量的一个模块,如果你有需要额外的环境变量设置,可以参考 /etc/security/pam_env.conf 这个文件的详细说明。
pam_unix.so: 这是个很复杂且重要的模块,这个模块可以用在验证阶段的认证功能,可以用在授权阶段的帐号授权管理, 可以用在会议阶段的登录文件记录等,甚至也可以用在密码更新阶段的检验!非常丰富的功能! 这个模块在早期使用得相当频繁喔!
pam_pwquality.so: 可以用来检验密码的强度!包括密码是否在字典中,密码输入几次都失败就断掉此次连线等功能,都是这模块提供的! 最早之前其实使用的是pam_cracklib.so 这个模块,后来改成 pam_pwquality.so 这个模块,但此模块完全相容于 pam_cracklib.so, 同时提供了 /etc/security/pwquality.conf 这个文件可以额外指定默认值!比较容易处理修改!
pam_limits.so: 还记得我们在第十章谈到的 ulimit 吗? 其实那就是这个模块提供的能力!还有更多细部的设置可以参考: /etc/security/limits.conf 内的说明。
了解了这些模块的大致功能后,言归正传,讨论一下 login 的 PAM 验证机制流程是这样的:
总之,就是依据验证类别 (type) 来看,然后先由 login 的设置值去查阅,如果出现“ includesystem-auth ” 就转到 system-auth 文件中的相同类别,去取得额外的验证流程就是了。然后再到下一个验证类别,最终将所有的验证跑完! 就结束这次的 PAM 验证啦!
经过这样的验证流程,现在你知道为啥 /etc/nologin 存在会有问题,也会知道为何你使用一些远端连线机制时, 老是无法使用 root 登陆的问题了吧?没错!这都是 PAM 模块提供的功能啦!
例题:为什么 root 无法以 telnet 直接登陆系统,但是却能够使用 ssh 直接登陆?答:一般来说, telnet 会引用 login 的 PAM 模块,而 login 的验证阶段会有 /etc/securetty 的限制! 由于远端连线属于 pts/n (n 为数字) 的动态终端机接口设备名称,并没有写入到 /etc/securetty, 因此 root 无法以 telnet 登陆远端主机。至于 ssh 使用的是 /etc/pam.d/sshd 这个模块, 你可以查阅一下该模块,由于该模块的验证阶段并没有加入 pam_securetty ,因此就没有/etc/securetty 的限制!故可以从远端直接连线到服务器端。
另外,关于 telnet 与 ssh 的细部说明,请参考鸟哥的 Linux 私房菜服务器篇
除了前一小节谈到的 /etc/securetty 会影响到 root 可登陆的安全终端机, /etc/nologin 会影响到一般使用者是否能够登陆的功能之外,我们也知道 PAM 相关的配置文件在 /etc/pam.d ,说明文档在 /usr/share/doc/pam-(版本) ,模块实际在 /lib64/security/ 。那么还有没有相关的 PAM 文件呢? 是有的,主要都在 /etc/security 这个目录内!我们下面介绍几个可能会用到的配置文件喔!
我们在第十章谈到的 ulimit 功能中, 除了修改使用者的 ~/.bashrc 配置文件之外,其实系统管理员可以统一借由 PAM 来管理的! 那就是 /etc/security/limits.conf 这个文件的设置了。这个文件的设置很简单,你可以自行参考一下该文件内容。 我们这里仅作个简单的介绍:
范例一:vbird1 这个用户只能创建 100MB 的文件,且大于 90MB 会警告
[root@study ~]# vim /etc/security/limits.conf
vbird1 soft fsize 90000
vbird1 hard fsize 100000
#帐号 限制依据 限制项目 限制值
# 第一字段为帐号,或者是群组!若为群组则前面需要加上 @ ,例如 @projecta
# 第二字段为限制的依据,是严格(hard),还是仅为警告(soft);
# 第三字段为相关限制,此例中限制文件大小,
# 第四字段为限制的值,在此例中单位为 KB。
# 若以 vbird1 登陆后,进行如下的操作则会有相关的限制出现!
[vbird1@study ~]$ ulimit -a
....(前面省略)....
file size (blocks, -f) 90000
....(后面省略)....
[vbird1@study ~]$ dd if=/dev/zero of=test bs=1M count=110
File size limit exceeded
[vbird1@study ~]$ ll --block-size=K test
-rw-rw-r--. 1 vbird1 vbird1 90000K Jul 22 01:33 test
# 果然有限制到了
范例二:限制 pro1 这个群组,每次仅能有一个使用者登陆系统 (maxlogins)
[root@study ~]# vim /etc/security/limits.conf
@pro1 hard maxlogins 1
# 如果要使用群组功能的话,这个功能似乎对初始群组才有效喔!而如果你尝试多个 pro1 的登陆时,
# 第二个以后就无法登陆了。而且在 /var/log/secure 文件中还会出现如下的信息:
# pam_limits(login:session): Too many logins (max 1) for pro1
这个文件挺有趣的,而且是设置完成就生效了,你不用重新启动任何服务的! 但是 PAM 有个特殊的地方,由于他是在程序调用时才予以设置的,因此你修改完成的数据, 对于已登陆系统中的使用者是没有效果的,要等他再次登陆时才会生效喔!另外, 上述的设置请在测试完成后立刻注解掉,否则下次这两个使用者登陆就会发生些许问题啦! _
如果发生任何无法登陆或者是产生一些你无法预期的错误时,由于 PAM 模块都会将数据记载在 /var/log/secure 当中,所以发生了问题请务必到该文件内去查询一下问题点!举例来说,我们在 limits.conf 的介绍内的范例二,就有谈到多重登陆的错误可以到 /var/log/secure 内查阅了! 这样你也就知道为何第二个 pro1 无法登陆啦!_
谈了这么多的帐号问题,总是该要谈一谈,那么如何针对系统上面的使用者进行查询吧? 想几个状态,如果你在 Linux 上面操作时,刚好有其他的使用者也登陆主机,你想要跟他对谈,该如何是好? 你想要知道某个帐号的相关信息,该如何查阅?呼呼!下面我们就来聊一聊~
如何查询一个使用者的相关数据呢?这还不简单,我们之前就提过了 id, finger 等指令了,都可以让您了解到一个使用者的相关信息啦!那么想要知道使用者到底啥时候登陆呢? 最简单可以使用 last 检查啊!这个玩意儿我们也在 第十章 bash 提过了, 您可以自行前往参考啊!简单的很。
Tips 早期的 Red Hat 系统的版本中, last 仅会列出当月的登陆者信息,不过在我们的CentOS 5.x 版以后, last 可以列出从系统创建之后到目前为止的所有登陆者信息!这是因为登录文件轮替的设置不同所致。 详细的说明可以参考后续的第十八章登录文件简介。
那如果你想要知道目前已登陆在系统上面的使用者呢?可以通过 w 或 who 来查询喔!如下范例所示:
[root@study ~]# w
01:49:18 up 25 days, 3:34, 3 users, load average: 0.00, 0.01, 0.05
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
dmtsai tty2 07Jul15 12days 0.03s 0.03s -bash
dmtsai pts/0 172.16.200.254 00:18 6.00s 0.31s 0.11s sshd: dmtsai [priv]
# 第一行显示目前的时间、开机 (up) 多久,几个使用者在系统上平均负载等;
# 第二行只是各个项目的说明,
# 第三行以后,每行代表一个使用者。如上所示,dmtsai 登陆并取得终端机名 tty2 之意。
[root@study ~]# who
dmtsai tty2 2015-07-07 23:07
dmtsai pts/0 2015-07-22 00:18 (192.168.1.100)
另外,如果您想要知道每个帐号的最近登陆的时间,则可以使用 lastlog 这个指令喔! lastlog会去读取 /var/log/lastlog 文件,结果将数据输出如下表:
[root@study ~]# lastlog
Username Port From Latest
root pts/0 Wed Jul 22 00:26:08 +0800 2015
bin **Never logged in**
....(中间省略)....
dmtsai pts/1 192.168.1.100 Wed Jul 22 01:08:07 +0800 2015
vbird1 pts/0 Wed Jul 22 01:32:17 +0800 2015
pro3 **Never logged in**
....(以下省略)....
这样就能够知道每个帐号的最近登陆的时间啰~ _
那么我是否可以跟系统上面的使用者谈天说地呢?当然可以啦!利用 write 这个指令即可。write 可以直接将讯息传给接收者啰!举例来说,我们的 Linux 目前有 vbird1 与 root 两个人在线上, 我的 root 要跟 vbird1 讲话,可以这样做:
[root@study ~]# write 使用者帐号 [使用者所在终端接口]
[root@study ~]# who
vbird1 tty3 2015-07-22 01:55 <==有看到 vbird1 在线上
root tty4 2015-07-22 01:56
[root@study ~]# write vbird1 pts/2
Hello, there:
Please don't do anything wrong... <==这两行是 root 写的信息!
# 结束时,请按下 [crtl]-d 来结束输入。此时在 vbird1 的画面中,会出现:
Message from [email protected] on tty4 at 01:57 ...
Hello, there:
Please don't do anything wrong...
EOF
怪怪~立刻会有讯息回应给 vbird1 !不过…当时 vbird1 正在查数据,哇! 这些讯息会立刻打断 vbird1 原本的工作喔!所以,如果 vbird1 这个人不想要接受任何讯息,直接下达这个动作:
[vbird1@study ~]$ mesg n
[vbird1@study ~]$ mesg
is n
不过,这个 mesg 的功能对 root 传送来的讯息没有抵挡的能力!所以如果是 root 传送讯息,vbird1 还是得要收下。 但是如果 root 的 mesg 是 n 的,那么 vbird1 写给 root 的信息会变这样:
[vbird1@study ~]$ write root
write: root has messages disabled
了解乎?如果想要解开的话,再次下达“ mesg y ”就好啦!想要知道目前的 mesg 状态,直接下达“ mesg ”即可!瞭呼? 相对于 write 是仅针对一个使用者来传“简讯”,我们还可以“对所有系统上面的使用者传送简讯 (广播)”哩~ 如何下达?用 wall 即可啊!他的语法也是很简单的喔!
[root@study ~]# wall "I will shutdown my linux server..."
然后你就会发现所有的人都会收到这个简讯呢!连发送者自己也会收到耶!
使用 wall, write 毕竟要等到使用者在线上才能够进行,有没有其他方式来联络啊? 不是说每个 Linux 主机上面的使用者都具有一个 mailbox 吗? 我们可否寄信给使用者啊!呵呵!当然可以啊!我们可以寄、收 mailbox 内的信件呢! 一般来说, mailbox 都会放置在/var/spool/mail 里面,一个帐号一个 mailbox (文件)。 举例来说,我的 vbird1 就具有/var/spool/mail/vbird1 这个 mailbox 喔!
那么我该如何寄出信件呢?就直接使用 mail 这个指令即可!这个指令的用法很简单的,直接这样下达:“ mail -s “邮件标题” username@localhost ”即可! 一般来说,如果是寄给本机上的使用者,基本上,连“ @localhost ”都不用写啦! 举例来说,我以 root 寄信给 vbird1 ,信件标题是“ nice to meet you ”,则:
[root@study ~]# mail -s "nice to meet you" vbird1
Hello, D.M. Tsai
Nice to meet you in the network.
You are so nice. byebye!
. <==这里很重要喔,结束时,最后一行输入小数点 . 即可!
EOT
[root@study ~]# <==出现提示字符,表示输入完毕了!
如此一来,你就已经寄出一封信给 vbird1 这位使用者啰,而且,该信件标题为: nice tomeet you,信件内容就如同上面提到的。不过,你或许会觉得 mail 这个程序不好用~ 因为在信件编写的过程中,如果写错字而按下 Enter 进入次行,前一行的数据很难删除ㄟ! 那怎么办?没关系啦!我们使用数据流重导向啊!呵呵!利用那个小于的符号 ( < ) 就可以达到取代键盘输入的要求了。也就是说,你可以先用 vi 将信件内容编好, 然后再以 mail -s “nice tomeet you” vbird1 < filename 来将文件内容传输即可。
例题:请将你的主文件夹下的环境变量文件 (~/.bashrc) 寄给自己!答:mail -s “bashrc filecontent” dmtsai < ~/.bashrc例题:通过管线命令直接将 ls -al ~ 的内容传给 root 自己!答:ls-al ~ | mail -s “myfile” root
刚刚上面提到的是关于“寄信”的问题,那么如果是要收信呢?呵呵!同样的使用 mail 啊! 假设我以 vbird1 的身份登陆主机,然后输入 mail 后,会得到什么?
[vbird1@study ~]$ mail
Heirloom Mail version 12.5 7/5/10\. Type ? for help.
"/var/spool/mail/vbird1": 1 message 1 new
>N 1 root Wed Jul 22 02:09 20/671 "nice to meet you"
& <==这里可以输入很多的指令,如果要查阅,输入 ? 即可!
在 mail 当中的提示字符是 & 符号喔,别搞错了~输入 mail 之后,我可以看到我有一封信件, 这封信件的前面那个 > 代表目前处理的信件,而在大于符号的右边那个 N 代表该封信件尚未读过, 如果我想要知道这个 mail 内部的指令有哪些,可以在 & 之后输入“ ? ”,就可以看到如下的画面:
& ?
mail commands
type <message list> type messages
next goto and type next message
from <message list> give head lines of messages
headers print out active message headers
delete <message list> delete messages
undelete <message list> undelete messages
save <message list> folder append messages to folder and mark as saved
copy <message list> folder append messages to folder without marking them
write <message list> file append message texts to file, save attachments
preserve <message list> keep incoming messages in mailbox even if saved
Reply <message list> reply to message senders
reply <message list> reply to message senders and all recipients
mail addresses mail to specific recipients
file folder change to another folder
quit quit and apply changes to folder
xit quit and discard changes made to folder
! shell escape
cd <directory> chdir to directory or home if none given
list list names of all available commands
指的是每封邮件的左边那个数字啦!而几个比较常见的指令是:
指令 意义
h 列出信件标头;如果要查阅 40 封信件左右的信件标头,可以输入“ h 40
d 删除后续接的信件号码,删除单封是“ d10 ”,删除 20~40 封则为“ d20-40 ”。 不过,这个动作要生效的话,必须要配合 q 这个指令才行(参考下面说明)!
s 将信件储存成文件。例如我要将第 5 封信件的内容存成 ~/mail.file:“s 5 ~/mail.file”
x 或者输入 exit 都可以。这个是“不作任何动作离开 mail 程序”的意思。 不论你刚刚删除了什么信件,或者读过什么,使用 exit 都会直接离开 mail,所以刚刚进行的删除与阅读工作都会无效。 如果您只是查阅一下邮件而已的话,一般来说,建议使用这个离开啦!除非你真的要删除某些信件。
q 相对于 exit 是不动作离开, q 则会实际进行你刚刚所执行的任何动作 (尤其是删除!)
旧版的 CentOS 在使用 mail 读信后,通过 q 离开始,会将已读信件移动到 ~/mbox 中,不过目前 CentOS 7 已经不这么做了! 所以离开 mail 可以轻松愉快的使用 q 了呢!
系统上面如果有一堆帐号存在,你怎么判断某些帐号是否存在一些问题?这时需要哪些软件的协助处理比较好? 另外,如果你跟鸟哥一样,在开学之初或期末之后,经常有需要大量创建帐号、删除帐号的需求时,那么是否要使用 useradd 一行一行指令去创建? 此外,如果还有需要使用到下一章会介绍到的 quota (磁盘配额) 时,那是否还要额外使用其他机制来创建这些限制值?既然已经学过 shell script 了, 当然写支脚本让它将所有的动作做完最轻松吧!所以啰,下面我们就来聊一聊,如何检查帐号以及创建这个脚本要怎么进行比较好?
先来检查看看使用者的主文件夹、密码等数据有没有问题?这时会使用到的主要有 pwck 以及pwconv / pwuconv 等,让我们来了解一下先!
pwck 这个指令在检查 /etc/passwd 这个帐号配置文件内的信息,与实际的主文件夹是否存在等信息, 还可以比对 /etc/passwd /etc/shadow 的信息是否一致,另外,如果 /etc/passwd 内的数据字段错误时,会提示使用者修订。 一般来说,我只是利用这个玩意儿来检查我的输入是否正确就是了。
[root@study ~]# pwck
user 'ftp': directory '/var/ftp' does not exist
user 'avahi-autoipd': directory '/var/lib/avahi-autoipd' does not exist
user 'pulse': directory '/var/run/pulse' does not exist
pwck: no changes
瞧!上面仅是告知我,这些帐号并没有主文件夹,由于那些帐号绝大部分都是系统帐号,确实也不需要主文件夹的,所以,那是“正常的错误!”呵呵!不理他。 _。 相对应的群组检查可以使用 grpck 这个指令的啦!
这个指令主要的目的是在“将 /etc/passwd 内的帐号与密码,移动到 /etc/shadow 当中!” 早期的 Unix 系统当中并没有 /etc/shadow 呢,所以,使用者的登陆密码早期是在 /etc/passwd 的第二栏,后来为了系统安全,才将密码数据移动到 /etc/shadow 内的。使用 pwconv 后,可以:
比对 /etc/passwd 及 /etc/shadow ,若 /etc/passwd 内存在的帐号并没有对应/etc/shadow 密码时,则 pwconv 会去 /etc/login.defs 取用相关的密码数据,并创建该帐号的 /etc/shadow 数据;
若 /etc/passwd 内存在加密后的密码数据时,则 pwconv 会将该密码栏移动到/etc/shadow 内,并将原本的 /etc/passwd 内相对应的密码栏变成 x !
一般来说,如果您正常使用 useradd 增加使用者时,使用 pwconv 并不会有任何的动作,因为 /etc/passwd 与 /etc/shadow 并不会有上述两点问题啊! _。不过,如果手动设置帐号,这个 pwconv 就很重要啰!
相对于 pwconv , pwunconv 则是“将 /etc/shadow 内的密码栏数据写回 /etc/passwd 当中,并且删除 /etc/shadow 文件。”这个指令说实在的,最好不要使用啦! 因为他会将你的/etc/shadow 删除喔!如果你忘记备份,又不会使用 pwconv 的话,粉严重呢!
chpasswd 是个挺有趣的指令,他可以“读入未加密前的密码,并且经过加密后, 将加密后的密码写入 /etc/shadow 当中。”这个指令很常被使用在大量创建帐号的情况中喔! 他可以由Standard input 读入数据,每笔数据的格式是“ username:password ”。 举例来说,我的系统当中有个使用者帐号为 vbird3 ,我想要更新他的密码 (update) , 假如他的密码是abcdefg 的话,那么我可以这样做:
[root@study ~]# echo "vbird3:abcdefg" | chpasswd
神奇吧!这样就可以更新了呢!在默认的情况中, chpasswd 会去读取 /etc/login.defs 文件内的加密机制,我们 CentOS 7.x 用的是 SHA512, 因此 chpasswd 就默认会使用 SHA512 来加密!如果你想要使用不同的加密机制,那就得要使用 -c 以及 -e 等方式来处理了! 不过从CentOS 5.x 开始之后,passwd 已经默认加入了 --stdin 的选项,因此这个 chpasswd 就变得英雄无用武之地了! 不过,在其他非 Red Hat 衍生的 Linux 版本中,或许还是可以参考这个指令功能来大量创建帐号喔!
由于 CentOS 7.x 的 passwd 已经提供了 --stdin 的功能,因此如果我们可以提供帐号密码的话, 那么就能够很简单的创建起我们的帐号密码了。下面鸟哥制作一个简单的 script 来执行新增用户的功能喔!
[root@study ~]# vim accountadd.sh
#!/bin/bash
# This shell script will create amount of linux login accounts for you.
# 1\. check the "accountadd.txt" file exist? you must create that file manually.
# one account name one line in the "accountadd.txt" file.
# 2\. use openssl to create users password.
# 3\. User must change his password in his first login.
# 4\. more options check the following url:
# 0410accountmanager.html#manual_amount
# 2015/07/22 VBird
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
# 0\. userinput
usergroup="" # if your account need secondary group, add here.
pwmech="openssl" # "openssl" or "account" is needed.
homeperm="no" # if "yes" then I will modify home dir permission to 711
# 1\. check the accountadd.txt file
action="${1}" # "create" is useradd and "delete" is userdel.
if [ ! -f accountadd.txt ]; then
echo "There is no accountadd.txt file, stop here."
exit 1
fi
[ "${usergroup}" != "" ] && groupadd -r ${usergroup}
rm -f outputpw.txt
usernames=$(cat accountadd.txt)
for username in ${usernames}
do
case ${action} in
"create")
[ "${usergroup}" != "" ] && usegrp=" -G ${usergroup} " || usegrp=""
useradd ${usegrp} ${username} # 新增帐号
[ "${pwmech}" == "openssl" ] && usepw=$(openssl rand -base64 6) || usepw=${username}
echo ${usepw} | passwd --stdin ${username} # 创建密码
chage -d 0 ${username} # 强制登陆修改密码
[ "${homeperm}" == "yes" ] && chmod 711 /home/${username}
echo "username=${username}, password=${usepw}" >> outputpw.txt
;;
"delete")
echo "deleting ${username}"
userdel -r ${username}
;;
*)
echo "Usage: $0 [create|delete]"
;;
esac
done
接下来只要创建 accountadd.txt 这个文件即可!鸟哥创建这个文件里面共有 5 行,你可以自行创建该文件!内容每一行一个帐号。 而是否需要修改密码?是否与帐号相同的信息等等,你可以自由选择!若使用 openssl 自动猜密码时,使用者的密码请由 outputpw.txt 去捞~鸟哥最常作的方法,就是将该文件打印出来,用裁纸机一个帐号一条,交给同学即可!
[root@study ~]# vim accountadd.txt
std01
std02
std03
std04
std05
[root@study ~]# sh accountadd.sh create
Changing password for user std01.
passwd: all authentication tokens updated successfully.
....(后面省略)....
这支简单的脚本你可以在按如下的链接下载:
http://linux.vbird.org/linux_basic/0410accountmanager/accountadd.sh