背景:由于产品需求,需要在嵌入式 Linux 平台提供安全的内部 FTP 服务,所以尝试选择 vsftpd + PAM 的方式实现。
vsftpd的全名是“Very Secure FTP Daemon”,显然,vsftpd的发展理念就是构建一个以安全为重心的FTP服务器。
(1)vsftpd尽量将服务取得的PID权限降低,使得服务即使不小心被入侵,入侵者也无法得到有效的系统管理权限,会使我们的系统较为安全。
(2)vsftpd支持chroot功能,也就是说它可以将某个特定的目录变成根目录,所以与该目录无关的其他目录就不会被误用。
(3)vsftpd可以通过配置文件(ftpusers和user_list)阻挡一些用户登录。
(4)vsftpd支持虚拟用户方式登录。
(5)vsftpd支持PAM认证模块插件,对登录账号和口令进行验证,并且可以根据需要扩展PAM认证模块。
(6)可通过支持SSL模块,增加对传输数据加密的功能。
可插拔认证模块PAM(Pluggable Authentication Modules)是一种认证机制,通过一些动态链接库和统一的API将系统提供的服务与认证方式分开,使得系统管理员可以根据需求灵活地调整服务程序的不同认证方式。简单来讲,PAM是一组安全机制的模块(插件),让系统管理员可以轻易地调整服务程序的认证方式,而不必对应用程序做任何修改。
PAM采用分层设计思想,可分为应用程序层、应用接口层、鉴别模块层,如下图所示。
PAM API 作为应用程序与鉴别模块层的连接纽带,让应用程序可以根据需求灵活地选择所需的鉴别功能模块。PAM 模块一般位于 /lib/security/ 目录下,PAM配置文件一般位于 /etc/pam.d/ 目录下。
注意:编译PAM模块前需要更新flex,否则会出现“yywrap未定义”等错误。(我们这里由flex 2.5.37升级到flex 2.6.0)
下面以Linux-PAM-1.1.1为例对PAM模块的编译与安装进行讲解。
(1)解压缩Linux-PAM-1.1.1.tar.bz2
# tar jxvf Linux-PAM-1.1.1.tar.bz2
(2)进入Linux-PAM-1.1.1目录
# cd Linux-PAM-1.1.1
(3)配置,生成Makefile
# ./configure --prefix=/usr --sysconfdir=/etc --libdir=/usr/lib --disable-regenerate-docu --enable-securedir=/lib/security --docdir=/usr/share/doc/Linux-PAM-1.1.1
(4)编译
# make
(5)安装
# make install
之后我们可以在 /usr/lib 目录下查看到如下库文件,它们就是上面所说的应用接口层。
root@imx6qrom5420:~# ls /usr/lib/libpam
libpam.la libpam_misc.la libpamc.la
libpam.so libpam_misc.so libpamc.so
libpam.so.0 libpam_misc.so.0 libpamc.so.0
libpam.so.0.82.2 libpam_misc.so.0.82.0 libpamc.so.0.82.1
在 /lib/security目录下查看到如下库文件,它们就是上面所说的鉴别模块层。
root@imx6qrom5420:~# ls /lib/security/pam_
pam_access.la pam_group.so pam_motd.so pam_succeed_if.so
pam_access.so pam_issue.la pam_namespace.la pam_tally.la
pam_debug.la pam_issue.so pam_namespace.so pam_tally.so
......
如果没有 /etc/pam.d 目录,则创建,并从其他Linux设备上拷贝 /etc/pam.d/other 和 /etc/pam.d/vsftpd 到目标机器。
vsftpd 的编译和安装步骤很简单,下面我们以 vsftpd-2.2.2 为例进行讲解。
(1)解压缩vsftpd-2.2.2.tar.gz
# tar zxvf vsftpd-2.2.2.tar.gz
(2)进入vsftpd-2.2.2目录
# cd vsftpd-2.2.2
(3)检查builddefs.h文件定义,选择是否支持tcp_wrappers、pam、ssl。
#undef VSF_BUILD_TCPWRAPPERS
#define VSF_BUILD_PAM
#undef VSF_BUILD_SSL
我们这里支持 PAM 即可,故不作修改。实际上 builddefs.h 这个头文件是由 sysdeputil.c 包含,有兴趣的话可以跟踪一下。
(4)编译
# make
(5)检查相关库
# ./vsf_findlibs.sh
这里的执行结果输出如下:
-lpam
-ldl
-lnsl
-lresolv
-lutil
/lib/libcap.so.2
(6)安装
可以执行 make install
进行安装,但是我的嵌入式平台不支持 install 命令,所以手动拷贝,如下:
# cp vsftpd /usr/sbin/
# cp xinetd.d/vsftpd /etc/xinetd.d/
# cp vsftpd.conf /etc/
(1) vsftpd预设以 nobody 作为此一服务执行者的权限,保险起见,执行如下命令:
# useradd nobody
useradd: user nobody exists
从输出的信息看来,nobody已经存在,那就放心了。
(2)vsftpd 预设 secure_chroot_dir
为 /usr/share/empty,所以我们需要创建 /usr/share/empty 目录,如下:
# mkdir /usr/share/empty/
mkdir: cannot create directory `/usr/share/empty': File exists
显然,我们这里已经存在empty目录了。
(3)匿名访问需要ftp用户,并且有合理的家目录(拥有者不是ftp本身,并且不开放写权限),创建ftp用户如下:
# mkdir /var/ftp/
# useradd -d /var/ftp ftp
保险起见,执行如下命令修改属性:
# chown root.root /var/ftp
# chmod og-w /var/ftp
FTP匿名访问模式是一种不安全的服务模式,在真实的应用场合中一定要注意,敏感的数据千万不要存放,以免泄密。但是要注意,vsftpd默认是允许匿名访问模式的,也就是说要禁止匿名访问,一定要显式声明 anonymous_enable=NO
,单纯注释掉 anonymous_enable=YES
是没有用的。
下面我们通过匿名访问模式登录 vsftpd,打开 vsftpd 的配置文件 /etc/vsftpd.conf,添加或修改下面参数:
anonymous_enable=YES (允许匿名访问模式)
anon_umask=022 (匿名用户上传文件的umask值)
anon_upload_enable=YES (允许匿名用户上传文件)
anon_mkdir_write_enable=YES (允许匿名用户创建目录)
anon_other_write_enable=YES (允许匿名用户修改目录或删除目录)
保存并退出vsftpd.conf文件,并重启vsftpd服务。
打开Windows的cmd命令行或者Linux的terminal,输入ftp回车,然后输入 open
进行连接。测试截图如下:
说明:anonymous访问的目录就是ftp的家目录 /var/ftp。实际应用中如果确实要提供匿名访问功能,一定要注意权限的管理,如:anon_upload_enable
、anon_mkdir_write_enable
、anon_other_write_enable
配置项。
本地用户模式有一些要注意的文件: ftpusers
、vsftpd.user_list
、vsftpd.chroot_list
。它们都是根据 vsftpd.conf 中相关配置提供一定的约束,有利于提高安全性。
可以认为:先检查 /etc/vsftpd.user_list,再检查 /etc/ftpusers进行PAM验证。所以,只要这两个文件任意一个配置了root用户,都会导致root无法登录。
本次测试的 vsftpd.conf 配置如下:
anonymous_enable=NO (禁止匿名访问模式)
local_enable=YES (允许本地用户模式)
write_enable=YES (设置可写权限)
local_umask=022 (本地用户模式创建文件的umask值)
userlist_enable=YES (配置禁止本地用户策略)
userlist_deny=YES
userlist_file=/etc/vsftpd.user_list
pam_service_name=vsftpd (指定pam配置文件,即 /etc/pam.d/vsftpd)
由于编译的时候选择了PAM,所以对于本地用户登录也需要指定PAM配置文件 /etc/pam.d/vsftpd,内容如下:
#%PAM-1.0
auth required /lib/security/pam_listfile.so item=user sense=deny file=/etc/vsftpd.ftpusers onerr=succeed
auth required /lib/security/pam_unix.so shadow nullok
auth required /lib/security/pam_shells.so
account required /lib/security/pam_unix.so
session required /lib/security/pam_unix.so
重启vsftpd服务,测试截图如下:
创建用户virtual,定义用户家目录并设置为不允许登录系统:
# useradd -d /var/ftproot -s /sbin/nologin virtual
为保证其他用户可以访问,执行:
# chmod -Rf 755 /var/ftproot
修改 /etc/vsftpd.conf 为:
anonymous_enable=NO (禁止匿名访问模式)
local_enable=YES (允许本地用户模式)
guest_enable=YES (开启虚拟用户模式)
guest_username=virtual (指定虚拟用户账号,也就是我们刚刚创建的virtual)
pam_service_name=vsftpd (指定pam配置文件,即 /etc/pam.d/vsftpd)
allow_writeable_chroot=YES (低版本vsftpd不需要)
#tcp_wrappers=YES (注释掉)
然后我们使用两个特殊的PAM模块来验证,分别是: pam_permit.so
和 pam_deny.so
,前者表示无条件允许,后者表示无条件拒绝。
修改pam配置文件 /etc/pam.d/vsftpd为:
auth required pam_permit.so
account required pam_permit.so
session required pam_permit.so
password required pam_permit.so
或者
auth required pam_deny.so
account required pam_deny.so
session required pam_deny.so
password required pam_deny.so
重新启动vsftpd,打开Windows的cmd命令行或者Linux的terminal,输入ftp回车,然后输入 open
进行连接。测试截图如下:
(1)/etc/pam.d/vsftpd配置为pam_permit.so无条件允许
(2)/etc/pam.d/vsftpd配置为pam_deny.so无条件拒绝
通过这样的测试,可以确定vsftpd使用虚拟用户 + PAM认证的框架生效,接下来就可以根据需求编写自己的PAM认证模块了。
可能出现的问题:
(1)“530 Login incorrect 登录失败”
账号、密码出错,没有权限或该用户被强制禁止访问等原因。
(2)本地用户或虚拟用户登录,出现“500 OOPS: priv_sock_get_result
远程主机关闭连接。”
可能由于用户密码错误,或库链接不正确(比如原本希望调用pam,却链接到crypt了)导致的。
(3)“500 OOPS: vsftpd: refusing to run with writable root inside chroot() 远程主机关闭连接”
配置文件vsftpd.conf中添加 allow_writeable_chroot=YES
,这个问题猜测跟版本有关,低版本vsftpd不需要该配置项,而有些版本则是 allow_writable_chroot=YES
。(注意:writeable和writable)
(4)“500 OOPS: priv_sock_get_cmd远程主机关闭连接”
(5)“500 OOPS: vsftpd: not found: directory given in ‘secure_chroot_dir’:/usr/share/empty”
找不到/usr/share/empty目录,需要创建该目录,或者修改 secure_chroot_dir
配置项。
注意:编译安装vsftpd,如果需要支持PAM模块,一定要先安装好PAM,否则会由于动态链接的问题造成异常。
编译完 vsftpd,可以通过执行 vsftpd 源码包中的 vsf_findlibs.sh 脚本来检查,或者使用 ldd 命令来检查链接情况。