一、vsftpd 简介
    Vsftpd是一个基于GPL发布的类UNIX系统的ftp服务器软件。其全称是Very Secure FTP Deamon,在安全性、速度和稳定性都有着不俗的表现。在安全性方面,vsftpd针对程序的权限来设计,以一般身份启动服务,对Linux系统的使用权限较低;在千兆以太网上,vsftpd的速度可以达到86MB/s;在稳定性上更是优秀,资料表明,完全工作24小时,传输数据达2.6TB,平均并发连接为1500用户,峰值达4000用户,而这些还是在单机上实现的。此外,vsftpd 还包括以下特性:
    基于IP的虚拟服务器
    虚拟用户,结合数据库的用户验证
    每个用户独立配置文件
    速率限制
    IPV6支持
    支持SSL加密传输
    ……

哪些站点在使用vsftpd
    以下站点一直在使用vsftpd(这仅仅是很少很少的一部分站点)
    ftp.redhat.com
    ftp.suse.com
    ftp.debian.org
    ftp.openbsd.org
    ftp.freebsd.org
    ftp.gnu.org
    ftp.gnome.org
    ftp.kde.org
    ftp.kernel.org
    rpmfind.net
    ftp.linux.org.uk
    ftp.gimp.org
    ftp-stud.fht-esslingen.de
    gd.tuwien.ac.at
    ftp.sunet.se
    ftp.ximian.com
    ftp.engardelinux.org
    ftp.sunsite.org.uk
    ftp.isc.org
    以上内容摘自vsftpd官方网站http://vsftpd.beasts.org/

二、软件安装和卸载

获得软件
    vsftpd目前最新版本为2.0.5,下载地址:ftp://vsftpd.beasts.org/users/cevans/vsftpd-2.0.5.tar.gz

软件安装
    解压软件,编辑builddefs.h文件
    # tar zxvf vsftpd-2.0.5.tar.gz
    # cd vsftpd-2.0.5
    # vi builddefs.h
    找到下面三行,其含义如右所示
    #undef VSF_BUILD_TCPWRAPPERS             //是否允许使用TCP Wrappers
    #define VSF_BUILD_PAM                    //是否允许使用PAM认证
    #undef VSF_BUILD_SSL                     //是否允许使用SSL

    如果要允许以上所示某项功能,使把undef改为define即可,注意每行前的“#“号不是注释,不能去掉(熟悉C语言的同志应该知道这个“#”是什么意思)。其中TCP Wrappers是一个验证IP地址合法性的程序,PAM认证让vsftpd支持本地用户登陆服务器,使用SSL可以建立一个加密的数据传输。这里我们把三项都启用。
    编译安装。如果系统中安装有旧版vsftpd,请先卸载它。默认安装执行文件在/usr/local/sbin中,man page放在/usr/local/man/man5与/usr/local/man/man8中。
    # make
    # make install

    将默认配置文件考贝到/etc/vsftpd/
    # mkdir /etc/vsftpd/
    # cp vsftpd.conf /etc/vsftpd/                   

    为了认vsftpd支持本地用户登录,我们将身份认证模块文件考入系统中。
    # cp RedHat/vsftpd.pam /etc/pam.d/vsftpd

    建立ftp用户及主目录:
    # mkdir /var/ftp
    # useradd -d /var/ftp ftp
  
    如果本来就已经存在ftp用户,则执行下面两条命令:
    # chown root:root /var/ftp
    # chmod 755 /var/ftp

    建立vsftpd需要的特殊目录:
    # mkdir /usr/share/empty/

软件卸载
    如果需要卸载,使用如下命令:
    # rm /usr/local/sbin/vsftpd
    # rm /usr/local/man/man5/vsftpd.conf.5
    # rm /usr/local/man/man8/vsftpd.8
    # rm /etc/xinetd.d/vsftpd
    # rm -rf /etc/vsftpd

三、配置vsftpd服务

服务的启动与停止
    启动服务之前,我们先编辑配置文件/etc/vsftpd/vsftpd.conf. 打开配置文件后可以看到许多以“#”开始的行,这些行都是注释行,大多是帮助信息,可以仔细阅读。vsftpd.conf文件的所有项目都是以“参数=值 ”来设置的,对格式要求比较严格,必须严格区分大小写,等号两边不能有空格,每行的最后也不能有空格。每个参数都有一个默认值,没有在配置文件中明确指定的参数就会使用默认值。我们这里不理会配置文件本来的信息,把所有内容都删掉或注释掉,最后加上下面四行,每行右边的//及后的文字是含义说明,不要输入到文件中:
    listen=yes                        //vsftpd工作在standalone 模式下
    anonymous_enable=yes                //允许匿名用户登陆服务器
    local_enable=yes                    //允许本地用户登录到服务器
    pam_service_name=vsftpd            //使用PAM认证

    vsftpd有两种工作模式,standalone模式和xinetd守护进程模式,第1行就是让其工作在standalone模式下。此种模式中,每次修改配置文件必须重新启动vsftpd服务才能生效,关于两种模式在后面有详细介绍。我们安装时还把 Redhat 目录下的 vsftpd.pam 文件复制成了/etc/pam.d/vsftpd 文件。这个文件就是本地用户登陆的 pam 验证配置文件。关于这个文件我们会在后面具体介绍。这里我们要知道,必须得有这个配置文件,而且主配置文件里要加上 pam_service_name=vsftpd语句,我们才能让本地用户登陆。用以下命令启动服务:
    # /usr/local/sbin/vsftpd /etc/vsftpd/vsftpd.conf &

    为保证服务确实启动,我们用如下命令检测:
    # netstat -an |grep 21
    tcp        0      0 0.0.0.0:21                  0.0.0.0:*                   LISTEN

    我们看到服务器已经打开了tcp21端口,表明ftp确实已经启动。再登录服务器:
    # ftp 127.0.0.1
    Connected to 127.0.0.1.
    220 (vsFTPd 2.0.5)
    530 Please login with USER and PASS.
    530 Please login with USER and PASS.
    KERBEROS_V4 rejected as an authentication type
    Name (127.0.0.1:root): ftp
    331 Please specify the password.
    Password:
    230 Login successful.

    这时我们已经用匿名用户(用户名ftp或anonymous,密码任意)登录到服务器了,还可以用本地用户登录。我们做测试时建议使用如上所示的ftp命令(windows、Linux及Unix都带这个命令,用法都是一样的)来登录服务器,这样可以看到更详细的信息,对于我们调试服务器是非常有帮助的。最简单的ftp服务器就已经达建起来了。使用如下命令关闭ftp服务:
    # killall vsftpd

服务启动脚本的制作
    在standalone 模式中,经常用上面的命令启动服务比较麻烦,我们做一个脚本来启动和停止服务。
建立一个新文件/etc/rc.d/init.d/vsftpd,把以下内容复制到文件中:

#!/bin/bash
#
# vsftpd      This shell script takes care of starting and stopping
#             standalone vsftpd.
#
# chkconfig: – 60 50
# description: Vsftpd is a ftp daemon, which is the program \
#              that answers incoming ftp service requests.
# processname: vsftpd
# config: /etc/vsftpd/vsftpd.conf
# Source function library.
. /etc/rc.d/init.d/functions
# Source networking configuration.
. /etc/sysconfig/network
# Check that networking is up.
[ ${NETWORKING} = “no” ] && exit 0
[ -x /usr/local/sbin/vsftpd ] || exit 0
RETVAL=0
prog=”vsftpd”
start() {
        # Start daemons.
        if [ -d /etc/vsftpd ] ; then
                for i in `ls /etc/vsftpd/*.conf`; do
                        site=`basename $i .conf`
                        echo -n $”Starting $prog for $site: “
                        /usr/local/sbin/vsftpd $i &
                        RETVAL=$?
                        [ $RETVAL -eq 0 ] && {
                           touch /var/lock/subsys/$prog
                           success $”$prog $site”
                        }
                        echo
                done
        else
                RETVAL=1
        fi
        return $RETVAL
}
stop() {
        # Stop daemons.
        echo -n $”Shutting down $prog: “
        killproc $prog
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$prog
        return $RETVAL
}
# See how we were called.
case “$1” in
  start)
        start
        ;;
  stop)
        stop
        ;;
  restart|reload)
        stop
        start
        RETVAL=$?
        ;;
  condrestart)
        if [ -f /var/lock/subsys/$prog ]; then
            stop
            start
            RETVAL=$?
        fi
        ;;
  status)
        status $prog
        RETVAL=$?
        ;;
  *)
        echo $”Usage: $0 {start|stop|restart|condrestart|status}”
        exit 1
esac
exit $RETVAL

    保存文件,再给该文件加上执行权限:
    # chmod 755 /etc/rc.d/init.d/vsftpd

    这样我们就可以通过下面的方法来管理服务了:
    # service vsftpd {start|stop|restart|condrestart|status}
    例如重新启动服务:
    # service vsftpd restart
    Shutting down vsftpd:                   [OK ]
    Starting vsftpd for vsftpd:             [OK ]

四、配置文件详解
    vsftpd配置文件只有一个,就是/etc/vsftpd/vsftpd.conf,上一小节我们就已经加入了两行参数了。在修改了配置文件之后,需要重新启动服务才能生效。下面我们就来详细解释其中的参数。

1. 匿名及本地用户共同参数
    write_enable=yes/no             //是否允许全局可写
    download_enable=yes/no            //是否允许所有用户可以下载
    dirlist_enable=yes/no             //是否允许所有用户可以浏览(列出文件列表)

    我们将write_enable=no、download_enable=yes两行加入配置文件,再测试:
    # ftp 127.0.0.1
    ……
    ftp> ls
    227 Entering Passive Mode (127,0,0,1,230,192)
    150 Here comes the directory listing.
    -rw-r–r–    1 0        0               4 May 13 11:43 ioo_file
    226 Directory send OK.
    ftp> get ioo_file
    local: ioo_file remote: ioo_file
    227 Entering Passive Mode (127,0,0,1,160,26)
    150 Opening BINARY mode data connection for ioo_file (4 bytes).
    226 File send OK.
    4 bytes received in 0.062 seconds (0.063 Kbytes/s)
    ftp> put scsrun.log
    local: scsrun.log remote: scsrun.log
    227 Entering Passive Mode (127,0,0,1,176,84)
    550 Permission denied.

    如上所示,我们看到了ftp上的文件,可以下载文件,但不可以上传文件。如果把dirlist_enable=no 也加上,便无法看到ftp上的文件(无法列出文件列表),但是如果你知道具体的文件名及路径,仍然可以下载文件的。实验结果就不贴上来了。

    再看下一组:
    ftpd_banner=欢迎语字符串
    banner_file=文件
    dirmessage_enable=yes/no
    message_file=文件
    参数ftpd_banner设置的欢迎语字符串将在登录时看到,如果想做出多行欢迎语,就要把内容单独存为banner_file参数指定的文件,应用中这两个参数二选一即可。dirmessage_enable和message_file参数是进入某个目录后显示的欢迎信息,用法与前两个参数一样。

2. 本地用户管理

2.1 本地用户常规配置参数
    local_root=/path                  //本地用户登陆服务器后直接进入的目录
    local_umask=八进制数       //本地用户上传档案权限的 umask值
    local_max_rate=数字            //本地用户传输速率单位为 bps
    chmod_enable=yes/no            // 是否允许本地用户改变ftp 服务器上档案的权限

    我们知道使用本地用户登录ftp后进入的是用户的主目录,locla_root这个参数允许我们登录服务器后直接进入其它的目录。此功能结合Apache 的userdir模块来实现网站内容更新上传是非常方便的。Linux系统中的任何文件都是有权限值的,上传的文件也不例外,这个默认的权限值就由 local_umask参数指定。其计算方法为:
    默认建立文件的权限+local_umask =0666
    默认建立目录的权限+local_umask =0777
    由此我们可以看出,上传的文件无论如何都不可能有执行权限的。这也是vsftp安全性的体现啊!
    local_max_rate参数限定了数据传输速率,包括上传和下载。chmod_enable参数限制用户是否可以改变档案权限(使用chmod, site命令)。

    我们可能想让为每个用户进行单独的配置,或者想配置个别用户的权限。这样就得为每个本地用户配置一个文件。这些配置文件必须是在同一个目录下,所以我们可以设置本地用户单独配置文件所在的目录:
    user_config_dir=/path        //用户单独配置文件所在目录

    我们在配置文件中加入以下几行:
    local_umask=077
    local_max_rate=20000
    user_config_dir=/etc/vsftpd/vsftpd_user_dir
    给予用户上传权限:
    write_enable=yes
    新建一个普通用户ioo,再新建一个目录/etc/vsftpd/vsftpd_user_dir,其下建一个文件ioo,里面加入下面几行:
    local_root=/var/www/html
    local_umask=022
    local_max_rate=50000
    把/var/www/html的所有者改为ioo:
    chown ioo:ioo /var/www/html
    测试之后我们发现,使用ioo用户登录后就直接进入var/www/html了,上传的文件(夹)权限为644(755),传输速率为50k,自定义的设置覆盖了主配置文件中的设置。

2.2 本地用户登录限制参数
    在我们的服务器上本来就有很多的本地用户,这些本地用户应该都是可以登陆 ftp 服务器的。但是 ftp 服务是以明文传输的,如果允许管理员登陆的话,这种机制显然不好。又或者我们想让一些本地用户可以登陆,或者一些不能登陆我们的 ftp 服务器,这样我们可以怎么设置呢?
    Vsftpd 提供了 userlist 功能。它使用一个文件来保存一些用户名,然后根据配置来决定是文件中的用户可以登录还是文件中没有列出的用户可以登陆 ftp 服务器,这样就对本地用户的登陆起到了限制作用。其配置参数有如下几个:
    userlist_enable=yes/no             //是否启用 userlist 功能模块
    userlist_deny=yes/no               //是否拒绝 userlist 文件中用户登陆 ftp 服务
    userlist_file=/path/to/file              //指定的 userlist 文件名
  
    当第1个参数值为yes时,第2、3行才起作用。我们将配置文件加上如下3行:
    userlist_enable=yes
    userlist_deny=yes
    userlist_file=/etc/vsftpd/vsftpd.userlist

    然后再新建一个文件/etc/vsftpd/vsftpd.userlist,在里面加入用户名,每个用户名一行,比如我这里加入用户root,再登录服务时出现以下信息:
    # ftp 127.0.0.1
    Connected to 127.0.0.1.
    ……
    Name (127.0.0.1:root): root
    530 Permission denied.
    Login failed.

    root用户已经不能登录了,在输入密码之前就被拒绝,但其它用户还可以登录的。如果把userlist_deny的值改为no,则只有文件中的用户才可以登录服务器。

2.3 本地用户的根目录参数
    大家再来看下面这一段:
    # ftp 127.0.0.1
    ……
    Name (127.0.0.1:root): ioo
    331 Please specify the password.
    Password:
    230 Login successful.
    Remote system type is UNIX.
    Using binary mode to transfer files.
    ftp> pwd
    257 “/home/ioo”
    ftp> cd /
    250 Directory successfully changed.
    ftp> ls
    227 Entering Passive Mode (127,0,0,1,163,193)
    150 Here comes the directory listing.
    drwxr-xr-x    2 0        0            4096 May 12 21:22 bin
    drwxr-xr-x    3 0        0            4096 Apr 30 19:57 boot
    drwxr-xr-x   12 0        0            3840 May 13 10:29 dev
    ……

    是不是感到吃惊了!我们用本地用户登录ftp服务器,却可以看到整个服务器的目录和文件,甚至还可以把/etc/passwd文件下载下来,这是非常不安全的,我们应该禁止这个功能,限制用户只能在自己的目录里浏览,这就要用到chroot功能。看下面三个参数:
    chroot_list_enable=yes/no             //是否启用 chroot_list 文件
    chroot_local_user=yes/no              //是否限制本地用户的根目录为自己的主目录
    chroot_list_file=/path/to/file                  //设置 chrootlist 文件名

    我们新建一个用户woo,再新建一个文件/etc/vsftpd/vsftpd.chroot_list,其中加入woo。然后在配置文件中添加以下几行:
    chroot_list_enable=yes
    chroot_local_user=yes
    chroot_list_file=/etc/vsftpd/vsftpd.chroot_list

    登录服务器测试:
    # ftp 127.0.0.1
    ……
    Name (127.0.0.1:root): woo
    331 Please specify the password.
    Password:
    230 Login successful.
    Remote system type is UNIX.
    Using binary mode to transfer files.
    ftp> pwd
    257 “/home/woo”

    Name (127.0.0.1:root): ioo
    331 Please specify the password.
    Password:
    230 Login successful.
    Remote system type is UNIX.
    Using binary mode to transfer files.
    ftp> pwd
    257 “/”

    我们看到,在文件中的用户woo根目录仍然是系统根目录,但文件外的用户根目录已经变成了“/”,就是说列在文件外的用户已经不能在自己主目录范围外浏览了。
    更改chroot_list_enable和chroot_local_user的值,得到以下几种组合:
参数     取值     取值     取值     取值
chroot_list_enable     yes     yes     no     no
chroot_local_user     yes     no     yes     no
意义     文件中列出的用户根目录为系统根目录,其它用户根目录为自己主目录。     文件中列出的用户根目录为自己主目录,其它用户根目录为系统根目录。     全部用户根目录都是自己主目录。     全部用户根目录都是系统根目录。

3. 匿名用户参数
    我们可以认为,本地用户进入自己主目录本身就应有比较大的权限,所以我们看到本地用户受限的语句不多。但是匿名用户通常涉及到一个公开、公共的互联网环境,所以限制匿名用户的权限语句就比较多,限制也比较细致。看看这一组参数:
    anon_upload_enable=yes/no           //是否允许匿名用户上传
    anon_mkdir_write_enable=yes/no      //是否允许匿名用户建立文件夹
    anon_other_write_enable=yes/no      //是否允许匿名用户可以使用除了建立文件夹和上传文件以外其他的ftp写操作命令。例如:delete、rename 等等
    anon_world_readable_only=yes/no      //匿名用户是否允许下载所有用户都可以访问的文件

    我们先把配置文件清理一下,现在配置文件只有下面几行:
    listen=yes  
    anonymous_enable=yes
    write_enable=yes
    download_enable=yes
    dirlist_enable=yes
    anon_upload_enable=yes
    anon_mkdir_write_enable=yes
    anon_other_write_enable=yes

    我们希望匿名用户有上传文件的权限,但这里还只是在ftp服务里给了写权限,还必须在文件权限考虑实现。于是我们给/var/ftp加上写权限:
    chmod a+w /var/ftp
    然后登录服务器:
    # ftp 127.0.0.1
    ……
    Name (127.0.0.1:root): ftp
    331 Please specify the password.
    Password:
    500 OOPS: vsftpd: refusing to run with writable anonymous root
    Login failed.
    421 Service not available, remote server has closed connection

    结果我们连服务器都登陆不了了!这是因为vsftpd出于在安全方面的考虑,不允许匿名用户对根目录有写权限。为此,我们只有去掉/var/ftp的写权限,再在其中新建一个目录,给予其写权限,让匿名用户上传文件到这个目录中。
    # chmod 755 /var/ftp
    # mkdir /var/ftp/upload
    # chmod 777 /var/ftp/upload

    现在我们可以匿名在upload里上传文件、建立文件夹、还可以删除改名等,但是却无法将上传的文件下载回来。如果你上传的文件是在一个新建的文件夹里,那么上传的文件不但下载不下来,而且连看都看不到:
    ftp> mkdir asm
    257 “/upload/asm” created
    ftp> cd asm
    250 Directory successfully changed.
    ftp> put file1
    local: file1 remote: file1
    227 Entering Passive Mode (127,0,0,1,135,56)
    150 Ok to send data.
    226 File receive OK.
    5 bytes sent in 0.063 seconds (0.077 Kbytes/s)
    ftp> get file1
    local: file1 remote: file1
    227 Entering Passive Mode (127,0,0,1,20,101)
    550 Failed to open file.
    ftp> ls
    227 Entering Passive Mode (127,0,0,1,133,186)
    150 Here comes the directory listing.
    226 Transfer done (but failed to open directory).
    ftp>
    这就要研究anon_world_readable_only这个参数了,它的默认值是yes。如果其值为yes,则匿名用户只可以下载所有用户都可访问的文件。比如:
    # ll
    total 40
    -rw—-r– 1 ftp ftp 34935 05-13 17:38 install.log
    -rw——- 1 ftp ftp   209 05-13 18:16 scsrun.log
    这里install.log文件权限其它位上有r权限,那么这个文件就可以被下载;scsrun.log文件其它位上没有任何权限,所以这个文件就不能被匿名用户下载。
    因此解决方法有两个,一是把参数anon_world_readable_only的值改为no;二是把上传文件默认权限的其它位上加上执行权限,这就用到下面这个参数:
    anon_umask=八进制数
    这个参数值的计算方法与本地用户local_umask参数一样,不在赘述。

    在以上的实验中,大家可能已经注意到匿名用户上传的档案所有者为ftp,这们也可能用下面两个参数来改变档案所有者:
    chown_uploads=yes/no         //是否开启修改默认匿名上传档案所有者的功能
    chown_username=本地用户名     //匿名上传档案的所有者名

    匿名用户使用任何密码都是可以登陆服务器的,那么我们可以免了匿名用户登陆必须输入密码的步骤,只要我们在配置文件中加入:
    no_anon_password=yes

    匿名用户的参数还有很多,我们就不一一介绍了

    到此,我们已经可以达建出的ftp服务器已经可以满足很多场合的需要了,如果有要求更加苛刻的场合,那就还需要进一步设置。

 

 
4. IP监听与连接控制
     vsftpd工作在独立模式(standalone)下的启动参数有两项:
     listen=yes/no
     listen_ipv6=yes/no
     其中第一条已经前面已经提过了,第二条应用在ipv6网络环境中,这两相参数只能有一条值为yes。
     在实际的网络环境中,服务器通常都有多个IP地址,而每个IP地址连接不同的网段,我们可能并不希望在所有网段的上的计算机都能访问服务器。而默认情况下,vsftpd将在所有的IP地址上监听,因此,我们需要下面两行:
     listen_address=监听 ip
     listen_address6=监听 ip
     这两行分别是针对IPv4和IPv6环境的。

     下面的两项是vsftpd并发连接控制:
     max_clients=数字
     max_per_ip=数字
     参数max_clients设置了服务器可以接受的最大并发连接数量,max_per_ip设置了每个客户端IP可以发起的最大连接数。针对服务器性能适当设置这两个参数,可以在服务器可接受的连接数量和连接速度之间找到平衡点。这两项默认值均为0,表示无限制。

     accept_timeout=数字
     connect_timeout=数字
     data_connection_timeout=数字
     idle_session_timeout=数字
     上面的数字都是以秒为单位的。其中 access_timeout 代表以 pasv数据连接模式的时候,数据连接的超时;connect_timeout 表示以 port模式连接数据连接时的超时时间;关于ftp服务连接模式下面会有详细介绍。data_connection_timeout 表示数据连接后数据连接等待的空闲时间超时,超过时间后,数据连接将断开连接;idle_session_timeout 设置发呆时间,也就是客户端隔多长时间不与服务器有交互 ftp 命令,将自动断开 ftp 服务连接。

5. 关于连接端口设置
     我们知道 ftp 服务有一点是不同于其他的服务的是,ftp 服务使用的是 tcp 双连接通道,也就是ftp-server 和 ftp-data 连接。我们可以这么理解:ftp-server 连接接受客户端连接请求、并发控制、身份和权限认证以及传输客户端下达的命令。ftp-data连接负责传输数据,也就是说当有数据传输的时候才会有这条连接。我们先来看一下 ftp-server 的设置:
     listen_port=端口号
     那么这条设置可以设置 ftp-server 端口号,默认为21,如果我们指定了其它端口号,那么客户端连接服务器上时就得使用指定端口号了。我们将这一行加入主配置文件中:
     listen_port=2121
     # ftp 127.0.0.1
     ftp: connect: Connection refused
     ftp> open 127.0.0.1 2121
     Connected to 127.0.0.1.
     220 (vsFTPd 2.0.5)
     530 Please login with USER and PASS.
     ……
     已经看到效果了,接下来我们讨论ftp-data连接的问题。
     FTP数据传输有两种模式:FTP Port模式和FTP Passive模式,两种工作方式截然不同。
     FTP Port模式
     在FTP Port模式下,客户端与服务器建立ftp-server连接之后,如果某条指令涉及到数据传送,就需要建立ftp-data连接。其实连接步骤如下:
     (1) 客户端启用另一个高于1024的空闲端口xx做连接准备,并且使用port命令利用ftp-server信道向服务器发送一个数据包,数据包里包含客户端的IP地址和xx端口,告诉服务器客户端xx端口已做好连接准备。Port命令还支持第三方(third-party)模式,第三方模式是客户端告诉服务器端打开与另台主机的连接。
     (2) 服务器以ftp-data端口(默认为20)主动向客户端xx端口进行连接。
     (3) 客户端响应服务器连接,并继续完成三次握手后,ftp-data连接建立,开始传送数据。当数据传输完毕后,服务器ftp-data端口就处于等待关闭状态。
     我们看到,Port模式下ftp-data连接请求是由服务器发起的。现在来看一下vsftpd中关于Port模式的语句设置:
     port_enable=yes/no                               //是否启用 port 模式
     connect_from_port_20=yes/no                    //port 模式下是否默认使用固定的 20 端口
     ftp_data_port=port_number                        //指定 port 模式的端口号
     port_promiscuous=yes/no                 //是否使用安全的 port 模式
     将port_enable 设置为 yes,就采用 port 模式。在 port 模式下端口如果采用固定的20 端口,就把connect_from_port_20选项就设为yes,这是很多服务器默认的设置规则。我们也可以指定其他的端口,那么这也就由ftp_data_port 来指定固定端口。port_promiscuous 默认值为no,表示ftp-data连接之前检验一下数据连接的目标ip 的是否是真正客户端的IP,反之则不检查。除非你确保服务器是与真正的客户端进行连接,否则不要将此参数值改为yes。
     我们将下面两行加入配置文件中:
     port_enable=yes
     connect_from_port_20=no
     登录服务器运行3次ls(显示文件列表)命令,再在服务器端运行netstat命令查看效果:
     # ftp 127.0.0.1
     Connected to 127.0.0.1.
     ……
     230 Login successful.
     Remote system type is UNIX.
     Using binary mode to transfer files.
     ftp> passive
     Passive mode off.
     ftp> ls
     ……
     226 Directory send OK.
     ftp> ls
     ……
     ftp> ls
     ……
     ftp> bye
     # netstat -an|more
     ……
     tcp 0 0 127.0.0.1:1069 127.0.0.1:1068 TIME_WAIT
     tcp 0 0 127.0.0.1:21   127.0.0.1:1065 TIME_WAIT
     tcp 0 0 127.0.0.1:1071 127.0.0.1:1070 TIME_WAIT
     tcp 0 0 127.0.0.1:1067 127.0.0.1:1066 TIME_WAIT
     ……
     我们看到服务器开了3个大于1024的端口去连接客户端,这3个连接就是3次ls命令产生的。如果我们把connect_from_port_20参数的值改为yes,再做上述实验,就会发现服务器3次连接都固定打开20端口。如果我们再加上ftp_data_port=2020语句,再做上述实验,服务器就会固定开启2020端口。

[attach]167[/attach]

    大家来看这张图,局域网客户机使用私有IP地址,并采用出口路由器做的 NAT(网络地址转换)连到 internet。ftp 服务器采用的是port模式。当客户端使用port命令向服务器发出包含自身IP地址和端口的包,此包通过路由器时路由器必须检查其内容,把包中的IP地址和端口翻译成分配给客户的地址和端口,而这个操作要求路由器必须工作在应用层!我们不可能要求每个客户的路由器都有此功能,但如果路由器没有正常完成这步工作,ftp数据传输就失败了。
     另外,由于第三方third-party模式的支持,黑客还可能在PORT命令中设置IP地址和端口号参数来指定一台其它主机的地址和端口号来发动攻击(称为FTP反弹攻击)。虽然有的防火墙设备已经修正了该问题,但对于大多数防火墙和路由器来说这个问题还是一个非常严重的。

     FTP Passive模式
     下面描述了Passive模式的ftp-data连接建立的步骤:
     (1) 当用户请求数据传输的时候,客户端软件发送PASV命令给服务器端表明客户端希望进入Passive模式;
     (2) 服务器端进行应答,应答数据包内有服务器的IP地址和一个临时端口,这个临时的端口是客户端在打开数据传输连接时应该使用的端口;
     (3) 客户端服务器发出连接请求,源端口为客户端自己选择的一个临时端口,目的端口为服务器在PASV应答命令中指定的临时端口号;
     (4) 服务器响应客户端请求,并继续完成TCP三次握手之后,ftp-data连接建立,开始传送数据。
     接下来我们来看看vsftpd中关于Passive模式中设置语句:
     pasv_enable=yes/no
     pasv_min_port=yes/no
     pasv_max_port=yes/no
     pasv_promiscuous=yes/no
     pasv_address=ip_address
     第一行设置是否启用 pasv 模式。pasv_min_port 和pasv_max_port 是设置在 pasv 模式下开启的端口范围。一般情况下,如果设置成 pasv 模式,我们最好指定端口范围,便于防火墙设置开启这个范围的端口以接受客户端的连接请求。pasv_promiscuous与port_promiscuous参数一样是设置在pasv下是否设置安全的传输,我们也要将其值设为no(默认值即为no)。pasv_address 后接一个有效的ip地址,来指定pasv打开端口的ip地址。但是默认情况下这行是取消的,因为服务会在 tcp 连接的套接字中知道连接的ip地址。我们把主配置文件改成这样然后看一下设置效果:
     listen=yes
     anonymous_enable=yes
     pasv_enable=yes
     pasv_min_port=3000
     pasv_max_port=3003

     登录服务器运行3次ls命令,再在服务器端运行netstat命令查看效果:
     # netstat -an
     ……
     tcp 0 0 192.168.0.105:3000 192.168.0.122:11066 TIME_WAIT
     tcp 0 0 192.168.0.105:3002 192.168.0.122:11065 TIME_WAIT
     tcp 0 0 192.168.0.105:3001 192.168.0.122:11063 TIME_WAIT

     我们看到,每次ls命令产生了一个连接,服务器开的端口是3000和3003之间。在 vsftpd2.0.3 以后的版本中, pasv 端口允许我们重复开启,在之前的版本里是不允许的,也就是说同一个端口可以同时与客户端建立多条连接,大家可以自己试验一下。
     大多数人认为在防火墙网络环境中Passive模式比Port模式的问题少,但我们注意到在Passive模式下,客户端向服务器端一个临时的目的端口发起连接,一些防火墙或者设备的访问控制列表(ACL)可能会阻止这种连接;同样服务器的回应也是从一个临时的端口到另一个临时的端口,防火墙或者访问列表也会阻止这种连接。在许多路由器和防火墙上(包括iptables)允许你使用访问列表关键字”established”来避免第二个问题,”established”关键字告诉路由器允许带ACK标志的包通过。而对于第一个问题,我们虽然使用pasv_min_port和pasv_max_port语句来限制服务器开设临时端口的范围,从而在防火墙上打开这些端口,但这样做还是存在一定的安全漏洞。好在多数状态检测防火墙例如Linux netfileters支持ftp协议的深层状态检测,进行准确的PASV动态端口过滤。

     五、vsftpd高级操作
     这一节我们介绍一下vsftpd的其它一些知识,其中包括:双模式切换、基于ip的虚拟ftp 站点、基于数据库虚拟用户建立及管理、基于 OpenSSL 的加密数据传输。
    1. vsftpd双模式切换
     前面我们提过,vsftpd 服务是支持在linux下的两种服务模式:独立(standalone)模式和守护进程(xinetd)模式。在standalone模式下,vsftpd进程启动后会一直占用系统资源,当有连接请求时,它会迅速反应;在xinetd模式下,ftp服务是由守护进程统一管理,当出现ftp连接请求时,守护进程才将ftp服务启动,这种方式最大的优点是没有连接的时候ftp服务不会占用系统资源,处于睡眠状态,但因为要花费时间去唤醒该服务,所以响应时间较长。
    之前做的试验一直是把vsftpd 做在独立模式下,下面我们把它移到守护进程下。注意在工作在守护进程下时,有的参数并不起作用,详细请查阅vsftpd帮助信息(man vsftpd.conf)。
     我们需要在守护进程配置目录/etc/xinetd.d/里建立vsftpd 的守护进程文件。我们在安装时已经将该文件考到/etc/xinetd.d/目录下了。我们稍稍修改其内容:
     #vi /etc/xinetd.d/vsftpd
     service ftp
     {
         disable             = no
         socket_type                = stream
         wait                        = no
         user                        = root
         server                = /usr/local/sbin/vsftpd
         server_args                = /etc/vsftpd/vsftpd.conf
         nice                        = 10
     }
     接下来把配置文件中的listen=yes或listen_ipv6=yes去掉,停掉vsftpd服务,重启xinetd服务:
     # sesrvice vsftpd stop
     # service xinetd restart
     重新启动守护进程后,vsftpd 服务就会让守护进程来管理了。在守护进程管理过程中,我们再修改主配置文件的话,就不需要重新启动服务了。

2. 基于IP的虚拟ftp站点
     这一小节我们来讨论在同一服务器上建立多个ftp站点,每个站点相互独立,拥有独立的配置文件。当然服务器必须有两个以上的IP地址。在standalone模式下,我们可以考虑启动多个ftp服务进程;在xinetd模式下,可以让守护进程来管理。下面我们分别讨论这两种情况。为简单起见,我们在服务器上建立两个匿名虚拟站点。下面是这两个站点的信息:
                               

                   站点1                                 站点2              
     ip地址                            192.168.0.105
          192.168.0.106 
    主配置文件                       /etc/vsftpd/vsftpd.conf 
       /etc/vsftpd/vsftpd2.conf 
    匿名用户映射的本地用户名 
             默认为ftp              新建立ftp2
    匿名用户主目录                             /var/ftp/  
              /var/ftp2/              

standalone模式下虚拟站点的建立
     首先我们修改站点1的配置文件/etc/vsftpd/vsftpd.conf为下面所示:
     listen=yes
     listen_address=192.168.0.105
     anonymous_enable=yes
     local_enable=yes
     pam_service_name=vsftpd
     write_enable=yes
     ftpd_banner=This is ftp1 site
     站点1就这样配置完毕了。接下来为站点2建立ftp2的用户:
     # mkdir /var/ftp2/
     # useradd -d /var/ftp2/ ftp2

     为了让站点2知道匿名用户的主目录为/var/ftp2,我们需要在配置文件中手动指定站点2使用ftp2用户登录,于是我们要用到下面这条语句:
     ftp_username=local_username
     建立站点2的配置文件/etc/vsftpd/vsftpd2.conf,内容如下:
     listen=yes
     listen_address=192.168.0.106
     anonymous_enable=yes
     local_enable=yes
     pam_service_name=vsftpd
     write_enable=yes
     ftp_username=ftp2
     ftpd_banner=This is ftp2 site
     接下来我们启动vsftpd服务,就可以看到效果了。需要注意的是这里就不能用service vsftpd start命令来启动服务了,必须用第三节前面介绍的方式启动。
     # /usr/local/sbin/vsftpd /etc/vsftpd/vsftpd.conf &
     [1] 2287
     # /usr/local/sbin/vsftpd /etc/vsftpd/vsftpd2.conf &
     [1] 2288

     # ftp 192.168.0.105
     Connected to 192.168.0.105.
     220 This is ftp1 site
     530 Please login with USER and PASS.
     ……
     # ftp 192.168.0.106
     Connected to 192.168.0.106.
     220 This is ftp2 site
     ……
     我们在各自的配置文件设置了不同的banner,上面已经看到效果了。

     守护进程模式下虚拟站点的建立
     在standalone模式下,IP的绑定在配置文件里配置了,配置文件名及其位置在启动服务的命令参数里指定;在xinetd模式下,这两步需要在守护进程配置文件里配置。这样在守护进程里面我们就得用到这两个设置:
     bind=绑定的ip
     server_args=每个站点的配置文件
     那么我们来做守护进程文件,首先来写站点1的守护进程文件:
     # vi /etc/xinetd.d/vsftpd
     service ftp
     {
         disable             = no
         socket_type       = stream
         wait              = no
         user              = root
         server            = /usr/local/sbin/vsftpd
         server_args       = /etc/vsftpd/vsftpd.conf
         nice              = 10
         bind              = 192.168.0.105
     }

     再建立站点2的守护进程文件,并输入下面的内容
     # vi /etc/xinetd.d/vsftpd2
     service ftp
     {
         disable             = no
         socket_type            = stream
         wait                   = no
         user                   = root
         server                 = /usr/local/sbin/vsftpd
         server_args           = /etc/vsftpd/vsftpd2.conf
         nice                   = 10
         bind                   = 192.168.0.106
     }
     这样两个守护进程文件就写完了,用户也在上个试验已经建立完了。接下来继续修改两个站点的配置文件,均是把listen、listen_address两行删去即可。现在我们可以重新启动xinetd服务,测试结果就不贴出来了。

3. 关于TCP Wrappers
        TCP Wrappers的作用,就是通过分析TCP网络数据包,根据其包头的IP地址和端口号,决定是否让这个数据进入到主机之中,因此我们也可以把它当成一个最内层的防火墙。数据包要进入ftp服务器,首先经过netfilter的过滤,通过TCP Wrappers筛选,守护进程(xinetd模式)限制,最后才能交由vsftpd进程来处理。
我们可以设置TCP Wrappers来限制某些主机能或者不能访问ftp服务器,这需要编辑两个文件:
        /etc/hosts.allow
        /etc/hosts.deny
        当数据包通过TCP Wrappers筛选时,/etc/hosts.allow文件会首先读取,然后再读取/etc/hosts.deny文件,就是说/etc/hosts.allow优先级要高一些。这两个文件设置规则如下:
        <service_name>:<IP, domain,hostname,network>:<allow|deny>
        <service_name>字段是服务名,也就是/etc/rc.d/init.d/目录下存在的文件名;第二个字段是可以是IP、域名、一台主机或者一个网段;第三个字段表示允许通过或者禁止。具体看下面的例子:
        vsftpd:192.168.0.111:allow        //允许主机192.168.0.111访问vsftpd服务器
        httpd:10.100.1.0/255.255.255.0:deny    //禁止10.100.1.0/24这个子网访问www服务
        最后一个字段的:allow或:deny可以省略,写在/etc/hosts.allow文件里默认就是allow, /etc/hosts.deny文件里默认是deny。其实我们可以把所有语句都写在hosts.allow文件中,但建议大家把allow和deny的语句分别写到两个文件之中。
        TCP Wrappers默认规则是允许所有数据包通过,所以大家在填写允许条目之后,一定还要加一条deny语句来拒绝其它的包。比如:
        # vi /etc/hosts.allow
        vsftpd:192.168.1.0/255.255.255.0        //允许192.168.1.0/24网段可以访问ftp
        # vi /etc/hosts.deny
        vsftpd:ALL                    //拒绝其它所有主机访问ftp

        如果vsftpd工作在守护进程模式下,不但可以受到TCP Wrappers的筛选,还可以设置守护进程配置文件来进行同样的过滤,这使用下面两条语句:
        only_from    = <IP, host, domain, network>        //限制可以访问的IP、主机、网络
        no_access = <IP, host, domain, network>    //限制不能访问的IP、主机、网络

        下面我们将only_from语句加入/etc/xinetd.d/vsftpd文件中,实现只有子网192.168.0.128/25才可以登录ftp服务器:
service ftp
{
        disable                 = no
        socket_type         = stream
        wait                      = no
        user                     = root
        server                  = /usr/local/sbin/vsftpd
        server_args         = /etc/vsftpd/vsftpd.conf
        nice                      = 10
        only_from             = 192.168.0.128/25
}

        如果vsftpd工作在standalone模式下,必须在编译vsftpd之前修改头文件builddefs.h以支持TCP Wrappers,详情请参加软件安装小节。另外还需要在vsftpd主配置文件中加入tcp_wrappers=yes参数。

4. 结合MySql数据库
        将vsftpd与MySql相结合,我们可以把用户和日志信息放到数据库里面去,如果再结合php来管理数据库,则可以使vsftpd管理更加方便。下面我们就来讨论一下基于MySql的虚拟户和日志功能。

        使用MySql来实现虚拟用户
        前面我们介绍了两类用户,关于匿名用户的语句比较多,权限限制比较灵活;而本地用户的特点是每个用户对应一个密码和主文件夹,登录控制比较灵活。可不可以将两者的优点结合一下呢?答案是肯定的,这就是虚拟用户。关于虚拟用户的启用,需要使用以下两条语句:
        guest_enable=yes/no
        guest_username=user_name

        上述guest_enable表示是否开启虚拟用户功能,guest_username表示虚拟用户登录后映射的本地用户名。如果开启虚拟用户功能,本地用户登录后将映射到guest_username参数指定的用户,主目录也变成该用户的主目录。
        我们修改vsftpd主配置文件,变为下面的样子:
        listen=yes
        anonymous_enable=yes
        local_enable=yes
        pam_service_name=vsftpd
        write_enable=yes
        dirlist_enable=yes
        download_enable=yes
        anon_upload_enable=yes
        anon_mkdir_write_enable=yes
        anon_other_write_enable=no
        anon_umask=073
        guest_enable=yes
        guest_username=virftp
        再新建一个用户virftp改变一下其主目录的权限:
        # useradd -d /var/virftp -s /sbin/nologin virftp
        # chmod 704 /var/virftp
        这时再以/etc/passwd中的用户登录ftp,通过查看内容,会发现主目录已经切换到/var/virftp/,可以下载和上传文件,但不能改名和删除。这就是说,限制匿名用户的参数也同样适用于本地用户。
        如果想让虚拟用户像本地用户那样拥有每个用户独立的主目录、独立的配置文件和权限,又该怎么办呢?还记得user_config_dir参数吧!在每个用户独立的配置文件里设定不同的权限和local_root参数就可以了。

        再来看看这个参数:
        virtual_use_local_privs=yes/no
        从刚才的实验可以看到,虚拟用户登录后,受到匿名用户参数的限制。我们可以通将此参数的值改为yes,则虚拟用户会变得和本地用户一样,拥有和本地用户一样的特权。由于我们平常都是看中了匿名用户限制参数比较细致这个特点才启用虚拟用户的,所以此参数默认值为no!

        下面我们结合MySql,把用户登录信息放到数据库里面去。首先我们需要安装MySql。
        到http://dev.mysql.com/downloads/mysql/5.1.html下载MySql,为了简单起见,我们下载在linux下已经编译好的版本。我下载的版本是mysql-5.1.18-beta-linux-i686-glibc23.tar.gz,然后照下面的步骤来安装MySql。
        # groupadd mysql
        # useradd -g mysql mysql
        如果系统中已经有了mysql用户,就可以省掉这步操作。

        # cd /usr/local
        # tar zxvf mysql-5.1.18-beta-linux-i686-glibc23.tar.gz
        # ln -s mysql-5.1.18-beta-linux-i686-glibc23 mysql
        如果原来系统中已经安装旧版mysql,最好将它卸载,除非你有足够的把握很好地面对系统中的两个mysql;通常习惯将mysql安装在/usr/local/mysql中,但为了将来版本升级的需要,建议使用链接方式使用mysql。

        # cd mysql
        # chown -R mysql:mysql .
        # scripts/mysql_install_db –user=mysql
        初始化mysql数据库,这步之前必须保证mysql用户有权限访问mysql目录。

        # chown -R root .
        # chown -R mysql data
        最好做这两步,至少官方文档是这么建议的。

        MySql的命令和man文档并不在PATH和MANPATH中,所以我们要手动加入。打开vi /etc/profile文件,找到export PATH …那一行,大约在40行左右,在它的前面加上一行:
        PATH=”$PATH”:/usr/local/mysql/bin
        打开/etc/man.config(有的版本为/etc/man.conf),在它的任何地方加入一行:
        MANPATH /usr/local/mysql/man

        到这时mysql就已经安装好了,通过这个命令启动mysql:
        /usr/local/mysql/bin/mysqld_safe –user=mysql &

        为了让mysql支持开机启动,执行下面的命令:
        # cp support-files/mysql.server /etc/rc.d/init.d/mysqld
        # chkconfig –add mysqld
        这样就可以让mysql以服务的方式开机启动,且还可以使用如下命令管理mysql服务:
        # service mysqld {start|stop|restart|reload|force-reload|status}

        启动mysql服务,检测下是否正常运行:
        # netstat -l|grep mysql
        tcp        0      0 *:mysql                     *:*                         LISTEN
        unix  2      [ ACC ]     STREAM     LISTENING     25534  /tmp/mysql.sock
        看到了吧,mysql已经在监听了,但它的socket file在/tmp/下,郁闷!

        默认情况下,mysql没有密码,任何人都可以登入。为了加强安全性,我们给mysql加上root帐号密码:
        # mysqladmin -u root password ‘your_P@ssw0rd’
        这样以后登录就需要密码了:
        # mysql -u root -p
        Enter password:
        Welcome to the MySQL monitor.  Commands end with ; or \g.
        Your MySQL connection id is 5
        Server version: 5.1.18-beta MySQL Community Server (GPL)
        Type ‘help;’ or ‘\h’ for help. Type ‘\c’ to clear the buffer.
        mysql>              

        建立数据库ftpdb:
        mysql> create database ftpdb;

        在数据库ftpdb中建立一个表ftpuser,表中有username和password字段:
        mysql> use ftpdb;
        Database changed
        mysql> create table ftpuser(username char(20) not null, password char(64) not null);
        Query OK, 0 rows affected (0.15 sec)
        这里要注意密码字段的长度,不同的算法生成的密文长度是不一样的,建议不要少于50位,否则可能导致密文在存储时被截短。

        我们插入两条记录作为两个ftp的登录名和密码,使用mysql自带的password函数来加密密码:
        mysql> insert into ftpuser values(‘test1’,password(‘abc’));
        Query OK, 1 row affected, 1 warning (0.08 sec)
        mysql> insert into ftpuser values(‘test2’,password(‘123’));
        Query OK, 1 row affected, 1 warning (0.00 sec)

        mysql> select * from ftpuser;
        +———-+——————————————-+
        | username | password                                  |
        +———-+——————————————-+
        | test1    | *0D3CED9BEC10A777AEC23CCC353A8C08A633045E |
        | test2    | *23AE809DDACAF96AF0FD78ED04B6A265E05AA257 |
        +———-+——————————————-+
        2 rows in set (0.00 sec)

        然后我们再建立一个可以让ftp服务登陆数据库的用户:
        mysql> grant select on ftpdb.ftpuser to ftpdb_query@localhost identified by ‘P@ssw0rd’;
        这个用户只能浏览 ftpdb 数据库下的 ftpuser 表中内容,我们如果想以后分配一个可以完全管理 ftpdb 数据库的用户,再这样:
        mysql> grant all on ftpdb.* to ftpdb_all@localhost identified by ‘P@ssw0rd’;

        到这里MySql的设置就算完了,接下来安装 PAM 基于 mysql 数据库的认证插件,我们可以到http:                    //sourceforge.net/projects/pam-mysql/下载得到。我下载的文件是pam_mysql- 0.7RC1.tar.gz。
        # tar zxvf pam_mysql-0.7RC1.tar.gz
        # cd pam_mysql-0.7RC1
        # ./configure –with-mysql=/usr/local/mysql
        # make
        # make install
        这里要注意两点,一是我们的mysql是手动安装在/usr/local/mysql下的,因此./configure命令需要指定mysql安装目录;二是安装好之后,pam_mysql.so被安装到了/usr/lib/security.  如果make过程出现错误,不要理会,只要产生 pam_mysql.so文件即可,我们可以直接将此文件考入/lib/security/目录。

        然后来编写PAM认证文件:
        # vi /etc/pam.d/ftpdb
        auth required /usr/lib/security/pam_mysql.so user=ftpdb_query passwd=P@ssw0rd host=localhost db=ftpdb table=ftpuser usercolumn=username passwdcolumn=password crypt=2
        account required /usr/lib/security/pam_mysql.so user=ftpdb_query passwd=P@ssw0rd host=localhost db=ftpdb table=ftpuser usercolumn=username passwdcolumn=password crypt=2
        注意,整个这个文件中只有auth和account两行内容,中途不要加回车换行;文件中不要再有包含其它pam模块的行;如果pam_mysql.so文件没有在/lib/security目录,就需要指定文件路径。文件中其它字段的意思是:
        user        访问数据库用户名
        passwd        访问数据库用户密码
        host        数据库主机
        db            数据库名
        table        表名
        usercolumn    用户列名
        passwdcolumn    密码列名
        crypt        密码验证机制;0 代表明文,1 代表DES 加密,2 代表Mysql的password()函数加密,3代表md5算法,4代表sha加密。

        现在我们修改vsftpd主配置文件/etc/vsftpd/vsftpd.conf,把pam_service_name的值改为ftpdb,也就是我们刚才建立的那个PAM认证文件的名字。再登录服务器测试,可以发现/etc/passwd中的用户已经无法登录到服务器了,而数据库中存储的用户名是可以登录的。我们还可以按前面所述的方法为每个虚拟用户建立独立的配置文件,指定独立的主目录;还可以结合apache服务建立php页面,然后实现让用户在web界面注册帐号、修改密码,这要比原来的认证方式灵活得多。

        在MySql中记录日志
        接下来我们看一下如何利用刚才的PAM 模块来结合mysql 数据库记录vsftpd 服务的日志。在介绍之前,我们来看看vsftp中关于日志记录的参数。
        xferlog_enable=yes/no            //是否启用 xferlog 日志格式
        xferlog_std_format=yes/no     //是否采用标准格式记录日志
        xferlog_file=/path/to/logfile            //xferlog 日志文件所在位置,默认为/var/log/xferlog
        上面的参数设置记录xferlog日志的格式。这是早期Wu-ftpd服务的日志格式,它会记录上传和下载的动作。vsftpd也有专有的日志格式,用下列参数设置:
        dual_log_enable=yes/no         //是否采用Vsftpd自己的日志记录方式
        log_ftp_protocol=yes/no          //是否记录所有的ftp命令日志
        vsftpd_log_file=/path/to/logfile     //指定vsftpd 日志文件位置,默认为/var/log/vsftpd.log

        xferlog_enable的默认值为no(vsftpd提供的配置文件模版将其值改为了yes),dual_log_enable的默认值也为 no,就是说默认情况下vsftpd是不记录日志的。我们也可以将日志信息写入系统日志/var/log/messages中,使用如下参数:
        syslog_enable=yes/no
        还是建议大家把日志记录为单独的文件,以便浏览和管理。实验比较简单,我们就不做了。

        对于使用文件来记录日志的方式来说,我们必须登录到服务器之后,才能查看日志。这样做不方便,不灵活。我们可以把日志放到数据库中,结合www服务建立一个浏览日志的页面,这样就非常舒服的看到web方式的日志了。下面来看看如何将日志写入数据库中去。
        首先必须使用虚拟用户所在的同一个数据库,新建立存储日志的表:
        mysql> create table ftplog
            -> (log char(100),
            -> user char(20),
            -> host char(20),
            -> time datetime,
            -> pid int);
        修改 PAM 配置文件,在原来的基础上我们改动一下:
        # vi /etc/pam.d/ftpdb
        auth required /usr/lib/security/pam_mysql.so user=ftpdb_all passwd=P@ssw0rd host=localhost db=ftpdb table=ftpuser usercolumn=username passwdcolumn=password crypt=2 sqllog=yes logtable=ftplog logmsgcolumn=log logusercolumn=user loghostcolumn=host logtimecolumn=time logpidcolumn=pid
        account required /usr/lib/security/pam_mysql.so user=ftpdb_all passwd=P@ssw0rd host=localhost db=ftpdb table=ftpuser usercolumn=username passwdcolumn=password crypt=2 sqllog=yes logtable=ftplog logmsgcolumn=log logusercolumn=user loghostcolumn=host logtimecolumn=time logpidcolumn=pid
        注意在这个文件中依然就是原来的两行,其中加入的内容分别对应如下:
        sqllog        表示是否将日志记录到数据库中
        logtable        记录日志的表名
        logmsgcolumn    记录日志信息的列
        logusercolumn    登录用户
        loghostcolumn    登录主机
        logtimecolumn    登录时间
        logpidcolumn    处理该用户连接的进程pid
        这次我换了一个登陆数据库用户,使用先前建立的有完全权限的用户ftpdb_all,这个用户才有在数据库中使用insert命令的权限。

5. 结合OpenSSL 实现加密数据传输
        FTP一个声名狼藉的问题是它以明文方式发送用户名和口令。任何人只要在网络中合适的位置进行抓包分析就可以看到用户名和口令;FTP发送的数据也是以明文方式传输,通过对ftp连接的监控和数据收集就可以重现ftp的数据传输。很多用户为了方便把相同的用户名和口令用在不同的应用中,如果黑客收集到FTP口令,他们也可能就得到了你在线帐号或者其他一些机密数据的口令。
        下面我们使用linux自带的抓包工具tcpdump抓包分析,来截取ftp登录用户口令:
# tcpdump -i eth0 -A
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
17:44:35.520880 IP 192.168.0.101.vytalvaultpipe > 192.168.0.105.ftp: S 3417937804:3417937804(0) win 65535 <mss 1460,nop,nop,sackOK>
E..0..@……..e…i    ………..p……………
17:44:45.681026 IP 192.168.0.105.ftp > 192.168.0.101.vytalvaultpipe: S 2518028758:2518028758(0) ack 3417937805 win 5840 <mss 1460,nop,nop,sackOK>
E..0..@.@……i…e..  ………p……………
17:44:35.520954 IP 192.168.0.101.vytalvaultpipe > 192.168.0.105.ftp: . ack 1 win 65535
E..(..@……..e…i    ………..P….x……..
建立TCP连接的三次握手,接下来登录之前信息,省约……

17:44:36.513224 IP 192.168.0.101.vytalvaultpipe > 192.168.0.105.ftp: P 32:44(12) ack 97 win 513 <nop,nop,timestamp 13091355 13088964>
E..4..@……..e…i    ………..P…v…USER test1
用户名:test1

17:44:39.942107 IP 192.168.0.105.ftp > 192.168.0.101.vytalvaultpipe: . ack 13 win 5840
E..(.|@.@..5…i…e..  ………P…….
17:44:39.942277 IP 192.168.0.105.ftp > 192.168.0.101.vytalvaultpipe: P 21:55(34) ack 13 win 5840
E..J.}@.@……i…e..  ………P….[..331 Please specify the password.
17:44:40.094629 IP 192.168.0.101.vytalvaultpipe > 192.168.0.105.ftp: . ack 55 win 65481
P….l………e…i    ……….
17:44:40.525157 IP 192.168.0.105.32832 > 192.168.0.1.domain:  31226+ PTR? 105.0.168.192.in-addr.arpa. (44)
E..H..@.@..;…i…..@.5.4..y…………105.0.168.192.in-addr.arpa…..
17:44:41.714630 IP 192.168.0.101.vytalvaultpipe > 192.168.0.105.ftp: P 13:23(10) ack 55 win 65481
P…….PASS abc…i    ……….
密码:abc

17:44:41.742271 IP 192.168.0.105.ftp > 192.168.0.101.vytalvaultpipe: P 55:78(23) ack 23 win 5840
….P….P..230 Login successful.
登录成功!

        怎么样!弄到密码很简单吧!服务器配置的再优秀,数据是明文传输的,所以还是会让别有用心之人有机可乘。

        SSL(Secure Socket Layer)工作于传输层和应用程序之间。作为一个中间层,应用程序只要采用SSL提供的一套SSL套接字API来替换标准的Socket套接字,就可以把程序转换为SSL化的安全网络程序,在传输过程中将由SSL协议实现数据机密性和完整性的保证。SSL取得大规模成功后,IETF将SSL作了标准化,并将其称为TLS(Transport Layer Security)。Ftp结合SSL,将实现传输数据的加密,保证数据不被别人窃取。
        要让vsftpd支持SSL,必须在安装之前修改头文件builddefs.h,将#undef VSF_BUILD_SSL行改为#define VSF_BUILD_SSL,在安装小节已经讲过。这里我们用OpenSSL结合vsftpd来实现数据加密传输。首先查看自己的系统有没有安装 OpenSSL,如果没有安装,到http://www.openssl.org/source/下载安装,安装过程很简单,就不贴出来了。
        下面我们为 vsftpd 生成证书:
# cd /etc/vsftpd/
# openssl req -new -x509 -nodes -out vsftpd.pem -keyout vsftpd.pem
Generating a 1024 bit RSA private key
…………………….++++++
………….++++++
writing new private key to ‘vsftpd.pem’
—–
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter ‘.’, the field will be left blank.
—–
Country Name (2 letter code) [GB]:cn
State or Province Name (full name) [Berkshire]:ShiChuan
Locality Name (eg, city) [Newbury]:ChenDu
Organization Name (eg, company) [My Company Ltd]:linuxer
Organizational Unit Name (eg, section) []:linuxer
Common Name (eg, your name or your server’s hostname) []:www.linuxer.cn
Email Address []:icecard@hotmail.com
        填写这些信息后,就产生了/etc/vsftpd/vsftpd.pem证书文件,接下来我们在配置文件中加入下面两行:
        ssl_enable=yes
        rsa_cert_file=/etc/vsftpd/vsftpd.pem

        现在我们登录服务器测试:
        # ftp 127.0.0.1
        Connected to 127.0.0.1.
        220 (vsFTPd 2.0.5)
        504 Unknown AUTH type.
        504 Unknown AUTH type.
        KERBEROS_V4 rejected as an authentication type
        Name (127.0.0.1:root): test1
        530 Non-anonymous sessions must use encryption.
        Login failed.
        已经不能登录了,可能是这个ftp客户端不支持ssl的原因吧!在windows下支持ssl的ftp客户端很多(IE是不支持的),比如 FlashFXP,使用比较简单,相信大家都会使用。我们这里介绍Linux下使用lftp来登录服务器。很多Linux发行版中都已经包含了lftp软件包,如果你的Linux中没有lftp,到http://lftp.yar.ru/get.html下载原码包来安装。
        # lftp 127.0.0.1
        lftp 127.0.0.1:~> user test1
        口令:
        lftp test1@127.0.0.1:~> ls
        -rw-r–r–    1 0        0               5 May 17 21:35 virftp.file
        drwxrwxrwx    2 500      500          4096 May 17 21:47 woo

        同时我们打开tcpdump抓包测试:
# tcpdump -i lo -A
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 96 bytes
省约部分信息……

23:32:15.237079 IP localhost.localdomain.ftp > localhost.localdomain.46605: P 36:47(11) ack 7 win 512 <nop,nop,timestamp 11554103 11554101>
..yI…U…..3………
..M7..M5 AUTH SSL
启用了SSL

23:32:15.272496 IP localhost.localdomain.46605 > localhost.localdomain.ftp: . ack 47 win 513 <nop,nop,timestamp 11554112 11554102>
…..U..yT….9……
..M@..M6
23:32:15.272570 IP localhost.localdomain.ftp > localhost.localdomain.46605: P 47:137(90) ack 7 win 512 <nop,nop,timestamp 11554112 11554112>
..yT…U……………
..M@..M@ AUTH TLS
EPRT
EPSV
MDTM
23:32:15.272605 IP localhost.localdomain.46605 > localhost.localdomain.ftp: . ack 137 win 513 <nop,nop,timestamp 11554112 11554112>
…..U..y…..9i…..
..M@..M@
23:32:15.273156 IP localhost.localdomain.46605 > localhost.localdomain.ftp: P 7:17(10) ack 137 win 513 <nop,nop,timestamp 11554112 11554112>
…..U..y……2…..
..M@..M@AUTH TLS
使用 TLS 认证方式,这是 Vsftpd 默认的安全认证方式。

        我们看到使用tcpdump抓到的包已经使用 TLS 加密了,数据也是加密的,再也不怕第三方窃听了。
上面的例子只使用了两条配置语句,vsftp还提供了下面的语句来设置ssl:
        ssl_enable=yes/no                 //是否启用 SSL,默认为no
        allow_anon_ssl=yes/no             //是否允许匿名用户使用SSL,默认为no
        rsa_cert_file=/path/to/file        //rsa证书的位置
        dsa_cert_file=/path/to/file        //dsa证书的位置
        force_local_logins_ssl=yes/no       //非匿名用户登陆时是否加密,默认为yes
        force_local_data_ssl=yes/no         //非匿名用户传输数据时是否加密,默认为yes
        force_anon_logins_ssl=yes/no    //匿名用户登录时是否加密,默认为no
        force_anon_data_ssl=yes/no    //匿名用户数据传输时是否加密,默认为no
        ssl_sslv2=yes/no                 //是否激活sslv2加密,默认no
        ssl_sslv3=yes/no                 //是否激活sslv3加密,默认no
        ssl_tlsv1=yes/no                 //是否激活tls v1加密,默认yes
        ssl_ciphers=加密方法              //默认是DES-CBC3-SHA

        大家自行加入其它语句来满足自己的需要,如有疑问参考http://vsftpd.beasts.org/、man vsftpd.conf,多多阅读软件源码包中的README文件。

六、FTP展望

        FTP是在70年代设计出来的,当时的互联网是一个封闭的网络,与现代网络环境还是有很大的差异,现代网络中不管你使用Port模式还是 Passive模式,都可能产生问题。很多人对FTP协议安全性进行不懈的努力,使用SSL/TLS进行ftp传输过程的验证和加密,基本解决明文传数据的问题。但还是存在不少缺陷,于是出现了一些FTP替代应用,如SCP、SFTP;如果你使用ftp更新你的网页,还可以考虑WebDAV。
        在FTP服务器软件中,vsftpd可以说是最安全的ftp软件,短小精悍,且高性能,是ftp服务器软件中的佼佼者。经过上面的学习,对于搭建安全高效的FTP服务器,再也不会困惑了。