Linux的文件和FHS & sudo的使用

一切皆文件. 你也是.

说到文件系统必然会扯到权限问题, 这篇笔记中就只拣相关的来说一说.

在不说到高级的权限管理的时候, Linux只有三种用户:

  • root – 万能的用户
  • 同一Group – 统一用户组的用户
  • Others – 其他用户

相关的文件就是 /etc/passwd (所有用户) /etc/shadow (用户密码) /etc/groups (所有用户组)

文件

文件属性

当时用ls -l查看目录的时候, 会列出文件的很多元信息. 其中就包括文件的类型和权限.

文件权限区域数一数的话, 一共有10个字符.

-|rwx|rw-|--- 1 root root 3231 Jun 4 17:21 install.log

其中第一个是用来显示文件的类型的, 这些:

  • - regular文件
  • d 目录
  • l 链接文件
  • b 块文件 设备文件中的可供存储的接口设备.
  • c 特殊字符文件 设备文件中的串行端口设备

接着的三个三个一组的就是文件权限属性, 常规的不说了.在这里说一说权限的重要意义.

Linux中文件权限的重要意义

对于文件

(r) 读权限,拥有此权限就可以进行文件的读取, 例如读取文本文件的文字内容等.
(w) 可以编辑,新增或者修改该文件的内容(不包括删除).

插一句, 我在做测试的时候发现删除文件的时候竟然不需要权限?奇怪?

rm

上面的情况理解了,文件删除的权限取决于该文件上级文件夹的权限.

rm2

(x) 改文件有被系统执行的权限.与Windows不同的是, Linux下全靠该权限来决定文件是否可被执行, 而不是文件后缀名, Linux的文件后缀名没有绝对的意义.

事实上,权限是对于文件内容而言的,与文件名的存在与否是没有什么关系的.

对于目录

目录的权限意义相比之下是比文件的权限更加重要的,所以也稍微复杂一点.目录也是文件呀!

一个一个来说一下:

(r) 代表你可以读取目录结构列表, 这也是目录的文件内容.简而言之, 具有读权限意味着你可以在目录中进行ls这样的操作.
(w) 这个是对于目录和目录中的文件的最重要的权限.包含下列动作:

  • 新建文件
  • 删除文件(不论该文件的权限如何), 这也是上面的疑问.
  • 进行对已存在的文件的重命名.
  • 转移该目录下的文件

凡是会发生变动的, 都是**(w)**权限赋予的.

(x) 目录不可以执行, 那这个权限的意义是什么呢? 就是把目录当做你的工作目录啦. 通俗的说, 你可以进入该目录.


接着说刚刚的文件元信息.

后面的数字是指这个文件的引用计数, 说白了就是这个文件内容的硬连接数. ( 此处存个疑问, 目录的连接数为什么这么多?或者说, 目录的连接计数是怎么计算的呢? )

更新:

链接计数到底是怎么计算的呢? 我们已经知道Linux通过inode号码来进行文件索引, 而进入目录其实就是打开目录文件的操作. 你会发现除了目录文件, 你所创造的新文件都是链接计数为1的. 如果你做了硬链接的操作的话, 这个计数就会加1. 那么现在就来解决之前的疑问吧. 在创建目录文件的时候, 系统会自动在里面创建两个硬链接: ... 这就是为什么你的新目录会有两个链接了, 其中一个是自己的硬链接啊!所以会自动加一. 因此 新的目录文件的连接数就是2. 同样的道理, 如果这个目录里面由一个目录的话, 他的连接数就是3 ( 2+1 ), 因为那个子目录也有一个**..**指向它.

因此,目录文件的连接数最小是2, 每创建个子目录,这个数字就加一.

接着,第三列和第四列分别是文件的所有者和所属组.

接着就是文件内容的大小,单位就是Byte.

再往后一栏是文件最近的修改时间.(默认显示)

最后就是这个文件的文件名.如果文件名前携带”.”, 则表明该文件为隐藏文件.

文件类型和扩展名

尽管Linux中的文件扩展名没有绝对的意义, 但他们可以方便管理者进行方便的辨识, 因此后缀名是作为一种规范而存在的.

先来说一下文件的种类.

之前说过的几种文件类型, 我们可以把文件划分成这些:

  • 普通文件 ( Regular File )

  • 纯文本文件 ( ASCII Text ) Linux系统中最多的文件种类

  • 二进制文件 ( Binary File ) 可执行文件

  • 数据格式文件 ( Data File ) 比如 /etc/wtmp 文件,直接读取是乱码,但是使用last命令可以读取.

  • 目录 ( Directory )

  • 第一个属性为d

  • 链接文件 ( Link )

  • 第一个属性为l, 虽然类似Win下的shortcut, 但是原理完全不一样.

  • 设备与设备文件 ( device )

  • 块设备文件

  • 特殊字符文件

  • 套接字文件 ( sockets )

  • 管道 ( FIFO, pipe )

关于Linux 的文件扩展名,一些基本的扩展名在这里说几个.

  • *.sh 脚本或者批处理文件
  • *.Z, *.tar, *.tar.gz, *.zip, *.tgz, 这些是经过打包的压缩文件.
  • *.cfg, *.conf 配置相关文件
  • *.php, *.html 网页相关文件
  • *.socket, *.sock 套接字文件
  • *.log 日志
  • *.pid 记载进程号的文件
  • *.so 库
    ….

文件的权限补充

先说一说文件的隐藏属性. 除了上面说过的那一排, 文件还存在一些隐藏属性.

先来查看一波:

1
2
3
4
5
6
[ root@Ubuntu ~]$ lsattr test
-------------e-- test
[ root@Ubuntu ~]$ chattr +i test
[ root@Ubuntu ~]$ rm -rf test
rm: cannot remove ‘test’: Operation not permitted
$ 即使是root也不能删除!

文件的隐藏属性有一些很有趣的属性,像上面的i, 就是指immutable该文件不可写,不可改变,不可删除.只有root才可以进行该选项的设置.

简单的列一些隐藏属性:

A 设置了A属性的文件,当对其进行访问时,他的访问时间(atime)并不会改变,这有利于减少磁盘的I/O.
a 设置了a之后, 这个文件只能增加数据,而不能删除或修改已有的数据 (只有root可进行设置)
c 这个 对于大文件似乎有点作用,在文件存储在磁盘的时候,会自动进行压缩, 在读取时会自动解压.
s 当设置了该属性, 文件数据会在删除的时候真的从磁盘上抹掉.

最后再说一说文件的SUID,SGID和SBIT权限. ( 其实我觉得不是很常用 ),而且这些权限在使用不当的情况下会造成很严重的后果.

SUID

SUID很好理解, 仅可用于二进制可执行文件, 当赋予了该权限的文件被执行的时候, 执行者将会获得该文件所有者的权限(其实都是直接视作所有者).

这个东西的一个最好理解的例子就是passwd命令.当你执行这个命令的时候, 你就获得了root权限(暂时), 然后就会有权限去修改passwd文件.

SGID

同理, SGID也只对二进制文件起效果, 执行者会暂时获得所属用户组的权限.

SBIT

SBIT仅对目录起效果,简单的说就是, 该目录下创建的所有文件和目录文件, 都不能被创建者和root以外的人删除掉.

这个三个权限的值分别为:4->GUID, 2->SGID, 1->SBIT


Linux目录配置标准 ( FHS )

这就是重点了, FHS的全称是( Filesystem Hierarchy Standard ), 正是因为Linux的使用者,开发者太多了, 如果每一个Distribution都有自己的一套, 那岂不是学习成本飞涨? 正因为此, 大家都遵循这个规范设计, 这样就可以保持同样的目录架构.

FHS是跟着历史不断改版的, 他将目录定义成下面的四种交互作用的形态.

可分享的(shareable) 不可分享的(unshareable)
不变的(static) /usr /etc
不变的(static) /opt /boot
可变动的(variable) /var/mail /var/run
可变动的(variable) /var/spool/news /var/lock

什么意思呢?

可分享的: 简单的说就是可以分享给其他系统( 网络其他主机 )挂载的目录

不可分享的: 本机上的设备文件或套接字, 这些都是本地机器有关.

不变的: 这些数据不会变动, 随distribution而不变动. 例如: 函数库, 文件说明, 系统管理员所管理的主机配置文件.

可变动的: 经常需要改变的数据, 例如登录文件, 新闻组等.

这是FHS中对于三层目录下应该放置什么数据, 分别是: / (root) , /usr (UNIX software resource)[以前一直以为是user, 其实这个和user没有什么关系], /var (variable) 与系统运作过程有关.

FHS中规定, 根目录所在的分区,应该越小越好,应用程序安装的软件应该尽量避免与根目录放在同一分区中,这样不仅性能更好,根所在的文件系统也不那么容易发生问题.

这是因为,根目录是整个系统最重要的一个目录.不但所有的目录都是从根目录中衍生出来的,同时根目录还是与开机,还原,系统修复等操作有关.由于开机时需要特定的开机软件.内核文件.开机所需程序.函数库等文件数据.因此当系统发生错误的时候,就需要根目录下的系统还原文件.

FHS定义出根目录下面要有这些子目录才好, 这些目录是:

/bin – 放置单用户模式下仍然能够被操作的命令, 主要有cat. chmod. chown, date. mv. mkdir. cp. bash等.
/boot – 放置开机会用到的文件,包括Linux的内核文件和开机菜单 所需的配置文件, 一般Linux的内核文件都是vmlinuz命名, 如果使用Grub进行的开机引导, 则还会存在/boot/grub这个目录
/etc – Linux下的配置文件所在地,几乎所有的配置文件都在这里,一般来说,该目录下的文件只有root可进行修改,FHS建议,该目录下不要存放可执行的二进制文件.
/home – 系统默认的用户家目录,默认的用户主文件会规范到这里来.
/lib – 这里放置了部分系统的函数库, 系统的函数库非常多,而这里放置的主要是系统开机的时候使用到的函数库以及/bin/sbin会用到的函数库.另外,该目录下放置了Linux内核相关的模块(/lib/module).
/media – 简单的说,该目录下放置的是可删除的设备文件 ( 光盘 软盘 等等 )
/mnt – 用来进行暂时挂载的目录,早期和/media的用途相同.
/opt – 用来放置第三方的软件,不过现在的软件都更习惯安装在/usr/local这个目录下.
/root – 系统管理员的主文件夹,一般会把他和根目录挂挂载在同一个分区,这是因为单用户维修模式下,用户会拥有root的主文件夹, 此时只挂载根目录.
/sbin – 只有root才有权限进行的命令.
/srv – 服务程序所需要取用的数据目录,似乎不常用.
/tmp – 一般用户或者正在进行的程序或者暂时放置文件的目录.FHS甚至建议在开机时清空/tmp目录

除了FHS的标准,大部分的发行版都会在根目录下增加/proc./sys以及/lost+found.

这些目录也需要了解, /proc目录本身就是一个虚拟文件系统, 他的所有内容都是在内存中的, 所以本身不占任何磁盘空间.该目录下记载了系统内存 进程 外部设备的状态和网络状态.

/sys/proc一样, 也是一个虚拟的文件系统,主要记录内核相关的信息:目前已加载的内核模块, 内核检测到的硬件设备信息.

loast+found仅在使用标准的ext2/ext3文件系统格式才会产生的目录.目的在于,当系统产生错误的时候, 会将一些丢失的目录片段丢到这里面.,当新装了一块硬盘的时候, 系统就会自动产生一个lost+found.

根据上面所说, 开机时会加载很多函数 配置文件 开始菜单等等…所以,绝对不能把以下目录和根分开:

  • /etc [配置文件]
  • /bin [重要执行文件]
  • /dev [设备文件]
  • /lib [函数库和内核模块]
  • /sbin [系统执行文件]

除了根目录, /var/usr下也有很多重要的子目录.

/usr下也有一个bin目录,和/bin的区别在于是否在开机时使用.

/usr/src 一般放置源码, 其中再深入一级的/linux放置的是Linux的系统源码.

/usr/include C/C++的头文件,当使用tarball的方式进行软件安装的时候,就会用到这其中的头文件与包含文件.

/usr/lib 是软件的函数库和目标文件;

/var目录是一个随着系统运行会逐渐变大的目录.因为缓存 登录文件 程序文件( lock pid )都会出现在这里面:

/var/cache程序运行时产生的暂存文件.

/var/log里面保存的是登录文件和一些程序的日志.

/var/run下会放置一些服务和程序的pid文件.

/var/lock这里是为了确保程序或者软件在同一时间仅会有同一人在进行访问或操作而加的锁所放置的目录.

sudo的简单使用

接触sudo之前, 我们知道有个叫su的命令. su其实就是switch-user. 使用时有点像SSH命令, 可以进行登录, 可以开一个子进程, 可以直接执行之后退出, 看下面的实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@WWW ~]# useradd test
[root@WWW ~]# su -l test
Last login: Mon Sep 25 20:04:14 EDT 2017 on pts/0
[test@WWW ~]$ logout
[root@WWW ~]# su test
[test@WWW root]$ logout
bash: logout: not login shell: use `exit'
[test@WWW root]$ exit
exit
[root@WWW ~]# su test -c 'ls -a ~/'
. .. .bash_history .bash_logout .bash_profile .bashrc .zshrc
[root@WWW ~]# su -l test -c 'ls -a ~/'
. .. .bash_history .bash_logout .bash_profile .bashrc .zshrc

使用-l参数就可以指定是否进行登录, 也可以使用-c参数直接执行.

由于su直接进行的用户切换, 在很多场景下是不合适的. 所以这个时候就要使用sudo 了.

sudo能够让一个用户不需要拥有另一个用户的密码就可以那个用户的权限运行. 只要另一个用户进行授权. 这样就可以进行操作的限制. 比su要灵活的. 这么一种机制的配置文件叫sudoer.

1
sudo [COMMAND]

我们来看看这个配置文件写了哪些内容.

1
2
3
4
5
6
7
8
9
10
11
12
[root@WWW ~]# cat /etc/sudoers
## Sudoers allows particular users to run various commands as
## the root user, without needing the root password.
##
## Examples are provided at the bottom of the file for collections
## of related commands, which can then be delegated out to particular
## users or groups.
##
## This file must be edited with the 'visudo' command.
...(omitted)
root ALL=(ALL) ALL
...(omitted)

这个文件主要定义了哪些用户可以得到什么操作的授权. 基本上就是上面的那种形式. 例如root的这一条就是说, 允许用户root任何主机上 以任何用户的身份, 执行所有命令.

1
2
[root@WWW ~]# sudo -u test whoami
test

使用-u参数可以具体制定以谁的身份执行.

继续往后看配置文件, root的下面有个奇怪的用户:

1
%wheel	ALL=(ALL)	ALL

前面由一个百分号(%) , 这个表示的是组名的标记, 也就是说这个组别的所有用户.

现在我们来做个实验, 可以新开一个SSH会话方便实验.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@WWW ~]# su -l test
Last login: Mon Sep 25 20:05:57 EDT 2017 on pts/0
[test@WWW ~]$ fdisk -l
[test@WWW ~]$ sudo fdisk -l

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 test:
test is not in the sudoers file. This incident will be reported.

他说test用户不在sudoers文件中, 此事将被报告.

但是我们有个wheel组, 所以现在试着把test加入进去.

1
[root@WWW ~]# usermod -a -G wheel test

再试一次:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
test@WWW ~]$ sudo fdisk -l
[sudo] password for test:
test is not in the sudoers file. This incident will be reported.
[test@WWW ~]$ logout
[root@WWW ~]# su -l test
Last login: Mon Sep 25 20:19:05 EDT 2017 on pts/1
[test@WWW ~]$ sudo fdisk -l
[sudo] password for test:

Disk /dev/sda: 21.5 GB, 21474836480 bytes, 41943040 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x000c83cb

仍然不行, 这是因为我们需要进行重新登录. 重新登录之后就可以进行了.

sudo的威力不知这样, 如果我们使用下面的命令, 影响是很大的:

1
2
3
[test@WWW ~]$ sudo su -
Last login: Mon Sep 25 20:19:07 EDT 2017 on pts/0
[root@WWW ~]#

无需使用密码, 就可以直接登录管理员账号. 这就是加入wheel组带来的权限. 显然我们是要针对不同的用户进行不同的权限设置的, 这个时候就应该去编辑sudoer文件. 在sudoer文件的开头写了这么一段话:

1
## This file must be edited with the 'visudo' command.

这样是可以检查语法错误的.

我们来试试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@WWW ~]# visudo 

80
81 ## Next comes the main part: which users can run what software on
82 ## which machines (the sudoers file can be shared between multiple
83 ## systems).
84 ## Syntax:
85 ##
86 ## user MACHINE=COMMANDS
87 ##
88 ## The COMMANDS section may have other options added to it.
89 ##
90 ## Allow root to run any commands anywhere
91 root ALL=(ALL) ALL
92 test ALL=(root) ALL

其实并没有这么死板, sudoer支持的写法有很多种:
第一配置项用户, 可以使用用户名, #uid, 用户别名, %用户组等等 如果有多个, 可以使用 逗号(,) 分割. 另外还支持取反, 使用!

用户别名也是在这个文件中进行定义的:

1
2
User_Alias NETADMIN = test, test2, test3
Cmnd_Alias NETWORKING = /sbin/route, /sbin/ifconfig, /bin/ping, /sbin/dhclient, /usr/bin/net, /sbin/iptables, /usr/bin/rfcomm, /usr/bin/wvdial, /sbin/iwconfig, /sbin/m ii-tool

除了用户可以进行别名定义, 还有命令,主机也可以进行. 要注意的是, 别名必须大写

主机名称也是多种多样的形式, 可以使用IP地址, 主机名, 网络地址(/掩码), 别名等 命令也是这样, 可以直接写命令名, 目录名(该目录下的所有命令都包括), 别名

到这里基本配置文件就是这样了, 现在来考虑这个问题, 如果现在经过了认证, 我如果想要立即再次执行同样的命令, 就要再次输入密码, 这样是不是有点繁琐了呢? 好在sudo考虑到了这个, 所以会提供一个免验证的令牌时间(5分钟) 只需要在一开始经过验证就行了.

sudo还提供了其他的功能, 例如:

1
2
3
4
5
6
7
8
9
[test@WWW ~]$ sudo -l
Matching Defaults entries for test on this host:
!visiblepw, always_set_home, env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS", env_keep+="MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS
LC_CTYPE", env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME
LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY", secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin

User test may run the following commands on this host:
(root) /sbin/route, /sbin/ifconfig, /bin/ping, /sbin/dhclient, /usr/bin/net, /sbin/iptables, /usr/bin/rfcomm, /usr/bin/wvdial, /sbin/iwconfig, /sbin/mii-tool
(ALL) ALL

这是当前登录的用户的权限一览. 如果这个时候你想要清除免密码的冷却, 使用sudo -k 就行了

有趣的是,sudo还可以进行密码提示语的自定义.

最后我们说一个禁止其他用户修改root密码的小技巧:

1
2
3
4
5
6
[test@WWW ~]$ sudo passwd
[sudo] password for test:
Changing password for user root.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.

怎么避免上面的情况呢?

我们可以这样写:

1
2
root	ALL=(ALL) 	ALL
test ALL=(root) /bin/passwd [a-z0-9]*, !/bin/passwd root

测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[test@WWW ~]$ sudo -l
[sudo] password for test:
Matching Defaults entries for test on this host:
!visiblepw, always_set_home, env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS", env_keep+="MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS
LC_CTYPE", env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME
LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY", secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin

User test may run the following commands on this host:
(root) /bin/passwd [a-z0-9]*, (root) !/bin/passwd root
[test@WWW ~]$ sudo passwd
Sorry, user test is not allowed to execute '/bin/passwd' as root on WWW.
[test@WWW ~]$ sudo passwd root
Sorry, user test is not allowed to execute '/bin/passwd root' as root on WWW.
[test@WWW ~]$ sudo passwd test
Changing password for user test.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.