文件共享服务 – 应用层的FTP, 工作在内核的nfs(难以跨平台), 跨平台的文件服务samba(在Linux上实现SMB).
先来说说我们的存储模型有哪些呢? DAS, NAS, SAN. 我们所使用的磁盘其实就是DAS模型, 而NAS其实就是专门用作存储的设备, 其内核对SAMBA, NFS等有特别的优化. 至于SAN, 他和NAS的区别就在于他是基于块的存储, 而NAS是基于文件共享的.
FTP协议
FTP的全称是(File Transfer Protocol), 也就是文件传输协议. 基于应用层协议来实现. 是一个C/S架构的协议. FTP是使用Socket进行的通信, 所以那也就会有端口的绑定. 但是FTP的连接却不像其他的服务一样(只监听一个端口), 它首先监听了21号端口. 但是FTP是很复杂的协议, 起码和HTTP协议比起来是复杂了很多. 因为它涉及到很多文件的管理功能. 例如创建, 改变权限, 复制, 移动等等. 所以他需要很多很多命令. 这样FTP就需要传输两类命令, 或者说两种数据:
- 命令连接: 用来进行命令的传输
- 数据连接: 用来传输用户所请求的上传和下载文件, 按需创建和关闭连接
当客户端想要进行访问FTP服务端的时候, 它先将请求发送到服务器端的21号端口. 鉴于之前说过的FTP需要传输两种数据. 所以他需要在接收到客户端命令之后(例如, 下载一个文件)从磁盘上获取, 接着另起一个连接. 这个连接和之前的session已经不是一样的了. 所以他需要再次通过一个套接字将数据发送. 发送结束之后, 结束这个新的Session.
同HTTP协议一样, FTP在传输非文本数据的时候也有问题, 例如图片, 如果启用字符传输机制, 对方可能看到的是一堆乱码. 而如果使用的是二进制的传输形式, 对方可能无法理解得到的结果. 所以FTP既支持文本传输也支持二进制传输格式, 这个是依靠传输数据的格式来判断的.
另外, FTP有个麻烦的模式: 主动和被动. 之所有会有这个, 是因为客户端请求下载一个文件的时候, 我们说需要一个新的数据连接, 这个连接谁来创建? 如果是服务器端, 这就是主动模式. 但是这个是存在一个问题的, 服务器端主动发送请求, 但是我们都知道一次连接是需要知道对方打开的端口是多少的. 这个时候客户端是随机的端口, 服务端怎么发送请求?
所以, 其实在主动模式下, 客户端选择一个随机端口. 连接服务器端的21号端口. 接着服务器端会将自己的20号端口去和客户端的随机端口+1的那个端口建立连接, 如果被占用就继续加一. 后者的这个连接上就是数据传输通道, 而前者就是命令传输端口. 其实在一开始命令连接的时候 客户端也会告诉服务器端, 自己打开的端口是哪一个.
然而, 这样是有很大的安全隐患的. 因为我们知道客户端主动打开了一个端口等着我, 如果加以构造, 服务端是可以随意向客户端发送数据的. 所以客户端是需要防火墙的, 但是这又出现了问题, 我们怎么知道开启那个端口使得服务端连接过来呢?
这么麻烦, 所以催生了后来的被动模式, 不管是命令还是数据传输都有客户端来负责建立连接. 这样客户端使用一个随机端口连接到服务器端的21端口, 接着再有客户端连接到20端口发起数据传输请求. 接着就可以将服务器端的随机端口托付过来传输数据就行了. 服务端发送过来端口形式是XXX,XX这样的形式 这个是需要进行计算的: XXX * 256 + XX.
可是, 服务器端也有防火墙啊? 他怎么知道服务器端的那个随机端口是哪一个呢? 关于这个, 在随后的iptables中在看. ( 连接追踪 )
简单的说说FTP协议, 接下来看看有那些典型的FTP软件:
- wu-ftpd
- proftpd
- pureftpd
- vsftpd
- ServU ( Windows )
以上是服务器端, 而客户端也有很多, 例如:
- wget
- lftp
- ftp
- curl
- filezilla
- gftp ( GUI )
FTP协议的响应码
1XX: 信息类的响应码
2XX: 成功类的响应码
3XX: 需要补充信息的状态码
4XX: 客户端错误
**5XX: ** 服务端错误
这个和HTTP的响应码几乎是一样的.
用户认证
现在想想我们之前说过的MySQL, 和FTP有一点像么? 当然他们组织数据的方式是不一样的, MySQl提供的是结构化的数据. 对于用户认证, MySQL提供的验证机制和系统是不一样的, 用户需要提供账号和密码, 这些信息是保存在一个数据表中的. 这种认证方式 我们把这个叫做虚拟用户 . 这个是为了和操作系统其他的资源隔离起来, 更加安全. 此用户仅仅是为了一个特定服务而存在.
与MySQL应用层服务不同的是, FTP协议存在的非常早, 在互联网混沌初开的时候就已经存在了. 早期, FTP使用的是系统用户进行认证. 其实也是因为FTP提供的是系统文件服务, 所以才选择这样的策略 – 关联系统账号.
现在的FTP已经完全支持虚拟用户了. 尽管默认使用的是系统用户, 但是可以非常方便的进行用户认证管理, 另外, 结合之前说过的MySQL, 我们还可以将用户账号信息丢到数据库中( 当然这里说的和mysql.user表是没有什么关系的 ). 这样更方便管理和组织.
Linux上平台独有的一些验证技术, 比如说之前说过的PAM模块, 如果我们的FTP程序使用这个做为用户认证的依赖, 那么移植到Win平台就比较困难. 所以为了通用和跨平台, 就需要FTP程序自我实现验证.
除了我们上面说的系统用户和虚拟用户, 还有一种匿名用户. 其实, 说到底虚拟用户和匿名用户还是系统用户的一个包装, 最终操作都需要系统用户去进行 这是由Linux系统决定的机制.
vsftpd
现在就来说说这个典型的FTP程序, 按照惯例, 安装完了之后我们先看看安装了哪些文件:
1 | [root@WWW ~]# rpm -ql vsftpd |
首先就看到了我们的日志滚动配置和PAM用户认证配置文件.
接着就是systemd的服务Unit. 主配置文件: /etc/vsftpd/vsftpd.conf
接着还有用户控制配置文件, 以及主程序. 最后的两个路径, 就像是HTTP服务的共享资源目录一样, 匿名用户的资源位置就是这个目录. 其实这个匿名用户就是ftp用户的映射:
1 | [root@WWW ~]# finger ftp |
其实就是家目录.
而虚拟用户就需要我们进行制定映射系统目录了 ( 系统用户的家目录 )
直接启动试试:
1 | [root@WWW ~]# systemctl start vsftpd.service |
现在服务已经起来了, 我们进入另外一台主机试试:
1 | [root@WWW ~]# ftp 192.168.56.101 |
我们使用匿名用户账号登录.(也可以使用ftp用户本身) 接着看到了2XX的成功信息:
1 | ftp> ls |
看到了pub目录, 还有进入了被动模式. 接下来快速的执行两次命令:
1 | ftp> ls |
端口是一次传输换一个, 另外这个时候快速的回到服务器端查看一下:
1 | TIME-WAIT 0 0 ::ffff:192.168.56.101:62420 ::ffff:192.168.56.103:41501 |
计算一下, 25 * 256 + 251 = 6651, 果然是吧.
可以使用help来查看所有支持的命令.
接下来我们要看看vsftpd的配置文件了.
vsftpd配置
1 | anonymous_enable=YES |
这几项都很好理解吧
我们在FTP服务器上进行一个用户的创建接下来看看:
1 | [root@WWW vsftpd]# useradd test |
接着我们在客户端尝试使用test用户进行连接:
1 | [root@WWW ~]# ftp 192.168.56.101 |
默认的目录会是自己的家目录, 这个我们之前就说过了, 但是, 在这情况下, 客户端可以随意的移动倒其他的目录, FTP是明文传输, 这样实在是太危险了. 所以我们必须把系统用户像匿名用户那样锁在一个目录中, 或者说根切换.
接下来我们按照用户类型看一下配置项:
匿名用户
anonymous_enable=YES
anon_upload_enable=YES 匿名用户能不能进行文件上传
anon_mkdir_write_enable=YES 匿名用户能否创建新目录
anon_other_write_enable=YES 匿名用户能否进行其他写操作
我们打开文件上传选项, 做个试验:
1 | ftp> put anaconda-ks.cfg |
尽管开启了选项但是似乎没有起到作用?? 其实不是, 如果这个选项没有开启, 收到的信息应该是Permission Denied. 但现在的这个消息说明了什么信息呢?
1 | drwxr-xr-x 3 root root 17 Sep 28 02:27 ftp |
没错, 是目录权限问题, 那怎么办? 直接修改权限? 肯定不行, 这样的话安全机制就荡然无存了呀, 所以这个时候我们可以这样做:
1 | [root@WWW ftp]# ls |
现在就可以了:
1 | [root@WWW ~]# ftp 192.168.56.101 |
但是:
1 | ftp> mkdir test |
无法创建目录, 这个时候就需要第三个选项了, 允许新建目录.
可是刚把目录创建好, 又遇到了新的问题. 这个上传的文件和目录怎么删除不掉啊!!
这就是第四个选项的功能, 启动之后再试:
1 | ftp> rmdir test |
就可以了.
以上的这些属性都是比较危险的选项, 开启时候要注意的.
本地用户的配置
local_enable=YES
write_enable=YES
local_umask=022
chroot_local_user=YES
allow_writeable_chroot=YES
chroot_list_enable=YES
chroot_list_file=/etc/vsftpd/chroot_list
关于这些属性的意味我们在实验中展示: [ 前三个就省略了, 看看都明白了 ]
1 | [root@WWW ~]# ftp 192.168.56.101 |
原先我们使用系统用户进行登录的时候, 会显示对方的家目录, 但是开启了第四个选项之后就可以看到被禁锢到根下了. 注意: 要和第五个选项同时开启,否则服务器会直接报错 拒绝连接
复原第四个选项, 我们开启第六和第七个选项, 先来创建一个新用户: ( 同样需要第五个选项 )
1 | [root@WWW ftp]# useradd test2 |
接着创建需要的那个列表文件:
1 | [root@WWW vsftpd]# echo "test2" > chroot_list |
好了, 现在进行验证:
1 | [root@WWW ~]# ftp 192.168.56.101 |
这就是指定chroot的用户.
接下来我们继续看几个配置:
dirmessage_enable=YES
这个选项允许你改变进入目录的提示信息, 在目录下新建一个.message文件就可以了, 有点像motd的感觉. 也有类似banner的选项就是: ftpd_banner=Welcome to blah FTP service.
xferlog_enable=YES 这个看名字也知道是什么了吧, 即使开启记录日志, 和这个配置相互配合的还有几个选项:
xferlog_file=/var/log/xferlog
xferlog_std_format=YES
其实就是日志保存位置和是否使用标准格式.
chown_uploads=YES
chown_username=whoever
是否修改上传文件的属主, 如果启用, 指定系统上存在的一个用户就行. 下面两个和连接相关:
idle_session_timeout=600
data_connection_timeout=120
都是关于超时的设定.
最后还有关于pam的设定, 我们看看
1 | [root@WWW ~]# ls /etc/pam.d/vsftpd -l |
后面还有一个userlist和一个tcp_wrapper. 先来说说这个userlist, 这个其实可以和pam归在一起. 可以使用这些选项来建立访问用户控制(白名单, 黑名单)
例如:
在userlist文件中加上test:
1 | # Users that are not allowed to login via ftp |
接着在另一台主机测试:
1 | [root@WWW ~]# ftp 192.168.56.101 |
我确定我的密码输入的是正确的, 但是会被deny.
这是一份黑名单, vsftpd借助pam模块实现. 但是其实他自己也有用户控制, 就是另外一个user_list文件. 这个更加灵活, 可以作为黑名单, 也可以作为白名单. 通过选项: userlist_deny=YES|NO
接着还有连接参数, 一些限制参数:
max_clients: 最大并发连接数
max_per_ip: 每个IP可同时发起的并发请求.
还有速率上的限制:
anno_max_rate: 匿名用户的最大传输限制, 字节/s
local_max_rate: 本地用户的最大传输限制, 字节/s
虚拟用户的配置
虚拟用户需要映射到一个指定的系统账号. 所以每一个虚拟用户访问到的就是这个系统账号的家目录. 但是我们却可以赋予这些虚拟用户不同的权限, 这是依靠匿名用户进行的设定.
关于账号的存储, 我们说过可以存储在文件中, 奇数偶数分别为用户名/密码. 这个文件为了安全会被Hash, 但是我的每一次修改都要进行Hash计算, 所以很不方便.
因此更好的方法是存储在数据库中. 而且这样还是即时的. 但是我们知道vsftpd是依靠于PAM的验证机制的, 如果想要访问数据库, 就需要PAM能够访问数据库. 这样就需要一个模块: pam-mysql, 这个模块是第三方的, 默认不会有, 所以就需要安装.
这个模块在epel源中, 如果搜索不到需要建立yum源, 如果实在搜索不到就使用二进制的rpm包吧.
安装完成之后会看到:
1 | [root@WWW ~]# yum list pam_mysql |
只要出现了so对象就可以了. 接着就可以继续了.
在pam_mysql的文档中, 我们可以看到官方给的一个示例:
1 | An example of the configuration file: |
后面还有更多的配置项. 接下来就直接来实现:
准备2台主机, 一台作为ftp服务器, 另外一台mysql服务器.
mysql准备好了之后进行用户添加和权限管理:
1 | MariaDB [mysql]> create database vsftpd; |
接着在vsftpd的主机上进行连接测试:
1 | [root@WWW ~]# mysql -u vsftpd -p -h 192.168.56.103 |
成功 ! 那我们继续. 接下来创建表和插入用户信息:
1 | MariaDB [vsftpd]> desc users; |
这一步也完成之后, 我们就可以建立pam认证所需的文件了.
关于文件的写法, 可以参考pam_mysql的说明文件.
完成后的pam文件应该是这样:
1 | auth required /lib64/security/pam_mysql.so user=vsftpd passwd=???? host=192.168.56.103 db=vsftpd table=users usercolumn=name passwdcolumn=password crypt=mysql |
接着在vsftpd的配置文件中指定:
1 | pam_service_name=vsftpd.mysql |
接着重启服务就完成了, 如果中间存在报错, 检查vsftpd的配置文件, 如果还不行, 查看secure日志吧.
以上就是连接mysql虚拟用户的配置. 接下来我继续谈这个, 我们在数据库中插入一个新的用户:
1 | +----+--------+-------------------------------------------+ |
现在的问题是, 我希望这些用户拥有不同的权限, 这怎么办呢?
对于这个问题, vsftpd的解决方法是在一个单独的user_config目录下, 一个用户一个配置文件来实现管理, 我们实际操作一下就行了:
比方说我们把这个单独的目录放在和主配置文件一样的位置, 就在配置文件中加上这个:
1 | user_config_dir=/etc/vsftpd/vusers |
接着创建之, 并且在里面写上对应的用户控制:
1 | [root@WWW vusers]# ls |
我是这样写的:
1 | [root@WWW vusers]# cat justin |
这样, justin用户可以进行写操作, 而bieber用户 不可以进行任何上传和写操作了.
实验效果略.
再插一句, 我们之前说过ftps是明文的, 而另外一个现在更加常用的文件传输 – sftp. 它是基于SSH协议的, 所以是加密传输的, 更加安全.