内网VSFTP服务器的搭建

  一、背景

    公司的一个产品需要提供上传功能,http不能满足上传速度需求,于是改用FTP上传。

  二、安装与配置

    1.安装命令:

        yum install vsftpd,安装完成后service vsftpd restart启动vsftp。

    2.配置:

    1).vsftp配置

      /etc/vsftpd/vsftpd.conf ,vsftp的主要配置文件,配置文件的全部我就不贴出来了,这里有详细的说明,我只说几个需要修改的参数:

        

        anonymous_enable=NO,是否允许匿名登录,因为我们有账号权限控制,所以不能允许匿名登录

        chroot_local_user=YES  

              chroot_list_enable=YES  

         chroot_list_file=/etc/vsftpd/chroot_list  这三个设置后不在chroot_list中的不给其浏览上层目录的权限。

         userlist_deny=NO

             userlist_file=/etc/vsftpd/user_list 上面两个参数化设置后,效果是只允许user_list中的用户登录

  

      我们知道FTP默认监听的通信端口为21,数据端口为20,但是,网上基本每时每刻都有人在扫这些常用端口,我就有一次在Window上搭建FTP,当时是测试一个功能,没有对配置文件做过多的更改,刚开了一个小时就被软件扫到了,在我网站根目录放了一个jsp文件,我自己调用了下,吓了一身冷汗,服务器目录信息基本都显示出来了,为了防止被这些“黑客”的软件扫到,还是自己设置一个端口吧。

        listen_port=8021   通信端口
        ftp_data_port=8020 数据端口
        pasv_enable=YES  被动模式开启
        pasv_addr_resolve=YES  被动模式是否用设置好的的地址返回给客户端,如果是NO,则从链接的套接字中自己获取地址,如果为YES,则设置为下面这个地址
        pasv_address=222.185.xxx.xxx  被动模式下返回的地址,安全需要,隐掉后面两个地址段
        pasv_min_port=10001   pasv模式下数据端口的下界
        pasv_max_port=10010  pasv模式下数据端口的上界
        local_max_rate=200000 用户传输速度限制,单位为bytes/second,0表示不限制

 

      service vsftpd restart ,重启生效

      2).防火墙配置

     在/etc/sysconfig/iptables中添加vsftp用到的端口

       

        -A INPUT -m state --state NEW -m tcp -p tcp --dport 8021 -j ACCEPT
        -A INPUT -m state --state NEW -m tcp -p tcp --dport 8020 -j ACCEPT
        -A INPUT -m state --state NEW -m tcp -p tcp --dport 10000:10010 -j ACCEPT

 

      service iptables restart ,重启生效

      3).添加VSFTP用户,且禁止用户用SSH登陆

      

      #adduser -d /home/ftp/bruce -g ftp -s /sbin/nologin bruce  新建vsftp用户,/home/ftp/bruce为此用户主目录(可自己随意设置),-g ftp此用户为ftp组员,/sbin/nologin 禁止此用户登录,bruce->用户名(可自己随意设置)

       #passwd bruce  为bruce设置密码

      最后用chmod命令给用户主目录赋予权限
          chmod 755 /home/ftp/bruce

 

 

  到此为止,我们的vsftp服务器的搭建完成了一半,下面是硬件的配置和客户端的编写。

    三、端口映射和客户端编写

    1.端口映射

      这个问题至关重要,也是整个服务器搭建过程中,困扰我最多的地方。在第二部配置完成和客户端编写完成后,我尝试着连接并查询目录中文件列表,客户端总是返回Connectiontimeout,从vsftp配置到程序检查了多遍,都是只能连接不能获取数据,后来我用抓包工具抓包,发现了问题所在,下面是程序错误的时候抓到的包:

        

 

1.[2013/4/27 星期六 16:04:13:129]
   220 (vsFTPd 2.2.2)


2.[2013/4/27 星期六 16:04:13:130]
   USER bruce


3.[2013/4/27 星期六 16:04:13:133]
   331 Please specify the password.


4.[2013/4/27 星期六 16:04:13:133]
   PASS bruce,2013


5.[2013/4/27 星期六 16:04:28:208]
   230 Login successful.


6.[2013/4/27 星期六 16:04:28:209]
   TYPE I


7.[2013/4/27 星期六 16:04:28:211]
   200 Switching to Binary mode.


8.[2013/4/27 星期六 16:04:28:247]
   PASV


9.[2013/4/27 星期六 16:04:28:249]
  227 Entering Passive Mode (192.168.1.122,39,16).

500 OOPS: vsf_sysutil_recv_peek: no data
500 OOPS: child died

   跑到第9步的时候卡住一会,接着报错java.net.ConnectException: Connection timed out: connect,出现上面最后两行500错误。第一反应就是返回的IP错了,因为我映射了外网端口,这里返回客户端的却是一个内网地址,于是加上上面提到过的参数

        

        pasv_addr_resolve=YES  被动模式是否用设置好的的地址返回给客户端,如果是NO,则从链接的套接字中自己获取地址,如果为YES,则设置为下面这个地址
        pasv_address=222.185.xxx.xxx  被动模式下返回的地址,安全需要,隐掉后面两个地址段

  再跑的时候返回的就对了,

      

227 Entering Passive Mode (222,185,xxx,xxx,39,24).

注解:括号里39,24表示连接的端口号,算法为:39*256+24=10008,端口落在vsftp.conf配置文件的上界和下界之间,说明端口设置生效了。可是,问题依然存在,看来不是IP地址这么简单的事情。那就只剩下端口号了,回去重新学习了FTP的两种工作方式:

    

主动FTP:

命令连接:客户端 >1024端口 -> 服务器 21端口(我们这里是8081,外网映射为15321)

数据连接:客户端 >1024端口 <- 服务器 20端口(我们这里是8020,外网映射为15320)

被动FTP(防止服务器主动去连的端口在防火墙后面,连接不上):

命令连接:客户端大于1024的端口 -> 服务器 21端口 (我们这里是8081,外网映射为15321)
数据连接:客户端大于1024的端口 -> 服务器上大于1024的端口(我们限定为10001-10010)

实际情况是,当时在公网对内网端口映射的时候,只映射了8021和8020两个端口,并没有映射vsftp.conf配置文件中的10001-10010,于是跟IT管理部门申请,映射了这10个端口,10001->10010这样严格映射,再执行客户端程序:

[2013/4/27 星期六 16:04:17:114]
220 (vsFTPd 2.2.2)

[2013/4/27 星期六 16:04:17:116]
USER bruce

[2013/4/27 星期六 16:04:17:118]
331 Please specify the password.

[2013/4/27 星期六 16:04:17:119]
PASS bruce,2013

[2013/4/27 星期六 16:04:32:203]
230 Login successful.

[2013/4/27 星期六 16:04:32:203]
TYPE I

[2013/4/27 星期六 16:04:32:206]
200 Switching to Binary mode.

[2013/4/27 星期六 16:04:32:236]
PASV

[2013/4/27 星期六 16:04:32:238]
227 Entering Passive Mode (222,185,xxx,xxx,39,24).

[2013/4/27 星期六 16:04:32:241]
LIST /

[2013/4/27 星期六 16:04:32:243]
150 Here comes the directory listing.
226 Directory send OK.

搞定!!

      2.客户端程序

      

import java.io.IOException;
import java.net.SocketException;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;


public class FTPTools {
    private FTPClient ftp=new FTPClient();
    /**
     * 连接ftp的方法
     * @param hostname 公网IP
     * @param port  公网端口
     * @param username  ftp用户名
     * @param password  ftp密码
     * @return
     */
    public boolean  connect(String hostname,int port,String username,String password){
        try {
            FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);
            conf.setServerLanguageCode("zh");
            ftp.configure(conf);
            ftp.setControlEncoding("GBK");//避免中文文件名乱码
            ftp.setConnectTimeout(150000);
            ftp.enterLocalPassiveMode();
            ftp.connect(hostname, port);
            int code=ftp.getReplyCode();
            if(FTPReply.isPositiveCompletion(code)){
                if(ftp.login(username, password)){
                    ftp.enterLocalPassiveMode();//切换成pasv被动模式
                    ftp.setFileType(FTP.BINARY_FILE_TYPE);//必须要,设置为2进制传输
                    ftp.setDataTimeout(60000);
                    ftp.setSoTimeout(120000);
                    FTPFile[] files = ftp.listFiles("/");
                    System.out.println(files.length);
                    for(FTPFile file:files){
                        System.out.println(file.getName());
                    }
                    return true;
                }
            }
            
        } catch (SocketException e) {
            e.printStackTrace();
            try {
                ftp.disconnect();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
            try {
                ftp.disconnect();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
        try {
            ftp.disconnect();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
        
    }
    public static void main(String[] args) {
        new FTPTools().connect("222.185.xxx.xxx", 15321, "bruce", "bruce,2013");
        
    }
    
}

 

好了,vsftp服务器的搭建就写到这里,客户端代码只放出了简单的连接测试代码,后面随着项目的推进,会把上传、下载、断点续传以及遇到的其它问题解决方法分享给大家,希望能够帮助到刚接触vsftp或者在这上面遇到问题的同学们。

 PS:后来发生个小插曲,我把目录指向到服务器挂载的磁盘阵列上面,能下载,但是死活传不上去,权限也赋了。后来知道了,在挂载机器上面给ftp用户chmod 777是没用的,只有用挂载磁盘的超级管理员赋权限才可以。

 

 

posted @ 2013-04-27 16:35  代码撕裂者  阅读(3603)  评论(0编辑  收藏  举报