• nginx正向代理SFTP整体配置方案


    一、概述
    目前由于行内网络规划以及安全的原因,不能直接从应用区域直接访问第三方SFTP文件服务器,只能允许代理服务器网络区域出去,也就是SFTP正向代理转发到第三方的SFTP文件服务器,而我们使用的代理应用软件则是开源的nginx。以前搞过正向代理HTTP,而没有尝试过正向代理SFTP,其实也就是TCP协议。为了满足应用需求,我们需要搭建nginx正向代理SFTP服务应用。

    nginx从1.9.0开始,新增加了一个stream模块,用来实现四层协议的转发、代理或者负载均衡等。这完全就是抢HAproxy份额的节奏,鉴于nginx在7层负载均衡和web service上的成功,和nginx良好的框架,stream模块前景一片光明。

    ngx_stream_core_module模块
    是模拟反代基于tcp或udp的服务连接,即工作于传输层的反代或调度器

    二、Nginx编译安装步骤

    卸载

    linux有一系列的软件管理器,比如常见的linux下的yum、Ubuntu下的apt-get等等。通过这些软件管理器可以很快的卸载软件,并且不会有文件及配置残留。这里我使用的是yum,命令如下

    yum remove nginx

    1、首先,要准备的是软件,可以在网上下载,http://nginx.org/en/download.html,我安装的是

    nginx-1.16.0  pgp

     

    2.解压并切换到安装目录

    tar -zxvf nginx-1.16.0.tar.gz

    cd nginx-1.16.0

    3.编译安装

    ./configure --prefix=/xjld/app/nginx --sbin-path=/xjld/app/nginx/sbin/nginx --conf-path=/xjld/app/nginx/conf/nginx.conf --with-http_stub_status_module --with-http_gzip_static_module --with-stream


    make && make install
    检测是否安装完成:出现如下,则说明安装成功

    Nginx启动和端口查看

    4.修改配置文件

    vim /xjld/app/nginx/conf/nginx.conf(在配置文件最后行添加如下)
    PS:这个模块一定要放在http外面

    s

    stream {
    upstream dnc_sftp {
    hash $remote_addr consistent;
    server 172.18.58.41:22 max_fails=3 fail_timeout=30s;
    }

    server{
    listen 8080;
    proxy_connect_timeout 5s;
    proxy_timeout 5s;
    proxy_pass dnc_sftp;
    }

    }

    解析:

    如上配置文件的含义为
    将端口8080反向代理NAME1组的serverIP:PORT,最大失败次数为3,超时时间为30秒;
    将端口60000反向代理NAME2组的serverIP:PORT,最大失败次数为3,超时时间为30秒。

    5.检测语法

    /opt/nginx/sbin/nginx -t
    

    6.开启NGINX

    /opt/nginx/sbin/nginx
    

    7.重启NGINX

    /opt/nginx/sbin/nginx -s reload
    

    这里推荐使用reload而不是restart。


    stream core 一些变量
    (注意:变量支持是从 nginx 1.11.2版本开始的)
    $binary_remote_addr
    二进制格式的客户端地址
    $bytes_received
    从客户端接收到的字节数
    $bytes_sent
    发往客户端的字节数
    $hostname
    连接域名
    $msec
    毫秒精度的当前时间
    $nginx_version
    nginx 版本
    $pid
    worker进程号
    $protocol
    通信协议(UDP or TCP)
    $remote_addr
    客户端ip
    $remote_port
    客户端端口
    $server_addr
    接受连接的服务器ip,计算此变量需要一次系统调用。所以避免系统调用,在listen指令里必须指定具体的服务器地址并且使用参数bind。
    $server_port
    接受连接的服务器端口
    $session_time
    毫秒精度的会话时间(版本1.11.4开始)
    $status
    会话状态(版本1.11.4开始), 可以是一下几个值:
    200
    成功
    400
    不能正常解析客户端数据
    403
    禁止访问
    500
    服务器内部错误
    502
    网关错误,比如上游服务器无法连接
    503
    服务不可用,比如由于限制连接等措施导致
    $time_iso8601
    ISO 8601时间格式
    $time_local
    普通日志格式的时间戳
    
     

    五.eclipse导入maven项目,修改配置文件,maven版本(apache-maven-3.3.3.rar)

    <dependency>
      <groupId>com.jcraft</groupId>
      <artifactId>jsch</artifactId>
      <version>0.1.55</version>
    </dependency>


    六.测试

    package dnc_util;

    import com.ctid.util.file.SFtpUtil;

    public class TestSftp {

    public static void main(String[] args) throws Exception {
    String host = "172.18.58.XX";
    String username = "appmanagXX";
    String password = "GBhnjm5XX";
    int port = 22;
    int timeOut = 10*1000;

    String localFile = "D:/sftptest/New-net.log";
    String destPath ="/xjld/test/te";

    SFtpUtil st =new SFtpUtil();
    st.connect(host, username, password, port, timeOut);
    st.upload(localFile, destPath, "old.log", "New.log");

    st.disconnect();

    }

    }

    sftp工具类

    package com.ctid.util.file;

    import java.io.File;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Vector;

    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;

    import com.ctid.core.exception.ServiceException;
    import com.ctid.core.util.StringUtil;
    import com.jcraft.jsch.Channel;
    import com.jcraft.jsch.ChannelSftp;
    import com.jcraft.jsch.ChannelSftp.LsEntry;
    import com.jcraft.jsch.JSch;
    import com.jcraft.jsch.JSchException;
    import com.jcraft.jsch.Session;
    import com.jcraft.jsch.SftpException;
    /**
    * 用于SFTP文件上传下载
    * @date 2019-06-14
    * @author 王伟
    */
    public class SftpUtil implements AutoCloseable{


    private final Log LOGGER = LogFactory.getLog(SftpUtil.class);

    //创建sftp通信通道
    private ChannelSftp sftp = null;
    private Session session = null;
    //文件分割符,默认为linux系统
    private static final String SEPARATOR = "/";


    //SFTP协议
    private final String SFTP_PROTOCAL = "sftp";
    //默认登陆超时时间15s
    private final int LOGON_TIME_OUT = 15*1000;
    // //默认session中socket超时时间30分钟
    private final int SOCKET_TIME_OUT = 30*60*60*1000;

    /**
    * 根据输入参数获取SFTP链接
    * @param host 主机IP
    * @param username 主机登陆用户名
    * @param password 主机登陆密码
    * @param port 主机ssh登陆端口,如果port <= 0取默认值(22)
    * @param timeOut 登录超时,通道连接超时时间
    * @throws Exception
    * @see http://www.jcraft.com/jsch/
    */
    public boolean connect(String host, String username, String password, int port,int timeOut) throws Exception {
    boolean result = false;
    Channel channel = null;
    JSch jsch = new JSch();
    if (timeOut < 0) {
    timeOut = LOGON_TIME_OUT;
    }
    session = createSession(jsch, host, username, port);
    // 设置登陆主机的密码
    session.setPassword(password);
    session.setTimeout(SOCKET_TIME_OUT);
    // 设置登陆超时时间
    session.connect(timeOut);
    try {
    // 创建sftp通信通道
    channel = (Channel) session.openChannel(SFTP_PROTOCAL);
    channel.connect(timeOut);
    sftp = (ChannelSftp) channel;
    //设置根路径,远程以及本地,对于window系统不适用,window需要写绝对路径到盘符
    sftp.cd(SEPARATOR);
    sftp.lcd(SEPARATOR);
    result = true;
    } catch (JSchException e) {
    LOGGER.error("exception when channel create. "+e.getMessage(), e);
    throw new ServiceException("exception when channel create. "+e.getMessage(), e);
    }
    return result;
    }

    /**
    * Private/public key authorization (加密秘钥方式登陆)
    * @param username 主机登陆用户名(user account)
    * @param host 主机IP(server host)
    * @param port 主机ssh登陆端口(ssh port), 如果port<=0, 取默认值22
    * @param privateKey 秘钥文件路径(the path of key file.)
    * @param passphrase 密钥的密码(the password of key file.)
    * @param timeOut 登录超时,通道连接超时时间
    * @throws Exception
    * @see http://www.jcraft.com/jsch/
    */
    public boolean connect(String username, String host, int port, String privateKey, String passphrase,int timeOut)
    throws Exception {
    boolean result = false;
    Channel channel = null;
    JSch jsch = new JSch();
    if (timeOut < 0) {
    timeOut = LOGON_TIME_OUT;
    }
    // 设置密钥和密码 ,支持密钥的方式登陆
    if (!StringUtil.isEmpty(privateKey)) {
    if (!StringUtil.isEmpty(passphrase)) {
    // 设置带口令的密钥
    jsch.addIdentity(privateKey, passphrase);
    } else {
    // 设置不带口令的密钥
    jsch.addIdentity(privateKey);
    }
    }
    Session session = createSession(jsch, host, username, port);
    // 设置登陆超时时间
    session.connect(timeOut);
    try {
    // 创建sftp通信通道
    channel = (Channel) session.openChannel(SFTP_PROTOCAL);
    channel.connect(timeOut);
    sftp = (ChannelSftp) channel;
    //设置根路径,远程以及本地,对于window系统不适用,window需要写绝对路径到盘符
    sftp.cd(SEPARATOR);
    sftp.lcd(SEPARATOR);
    result = true;
    } catch (JSchException e) {
    LOGGER.error("exception when channel create."+e.getMessage(), e);
    throw new ServiceException("exception when channel create."+e.getMessage(), e);
    }
    return result;
    }

    /**
    * upload the file to the server<br/>
    * 将本地文件名为 localFile 的文件上传到目标服务器, 目标文件名为 destFile,<br/>
    * 采用默认的传输模式: OVERWRITE 覆盖式推送
    * @param sftp
    * @param localFile 本地文件的绝对路径
    * @param destFile 目标文件的绝对路径
    * @throws ServiceException
    */
    public boolean upload(String localFile, String destFile) throws ServiceException {
    boolean result = false;
    try {
    File file = new File(localFile);
    if (file.isDirectory()) {
    for (String fileName : file.list()) {
    sftp.put(FileUtil.setDirectoryFile(localFile, fileName), destFile,ChannelSftp.OVERWRITE);
    }
    } else {
    sftp.put(localFile, destFile, ChannelSftp.OVERWRITE);
    }
    result = true;
    } catch (Exception e) {
    LOGGER.error(localFile+" upload to "+destFile+" exception "+e.getMessage(), e);
    throw new ServiceException(localFile+" upload to "+destFile+" exception "+e.getMessage(), e);
    }
    return result;
    }

    /**
    * upload the file to the server<br/>
    * 将本地文件名为 localFile 的文件上传到目标服务器, 目标路径为destPath,<br/>
    * 目标文件名称为destFileName
    * 采用默认的传输模式: OVERWRITE 覆盖式推送
    * @param sftp
    * @param localFile 本地文件的绝对路径
    * @param destPath 目标文件的绝对目录路径
    * @param destFileName 目标文件名称
    * @throws ServiceException
    */
    public boolean upload(String localFile, String destPath,String destFileName) throws ServiceException {
    boolean result = false;
    String destFile = "";
    try {
    //创建远程路径
    createRemotedir(destPath);
    //拼接远程路径+文件名
    destFile = FileUtil.setDirectoryFile(destPath, destFileName);
    sftp.put(localFile,destFile,ChannelSftp.OVERWRITE);
    result = true;
    } catch (Exception e) {
    LOGGER.error(localFile+" upload to "+destFile+" exception "+e.getMessage(), e);
    throw new ServiceException(localFile+" upload to "+destFile+" exception "+e.getMessage(), e);
    }
    return result;
    }


    /**
    * upload the file to the server and rename file<br/>
    * 将本地文件名为 localFile 的文件上传到目标服务器, 目标路径为destPath,<br/>
    * 目标文件名称为destFileName,重命名后的文件名称为newFileName
    * 采用默认的传输模式: OVERWRITE 覆盖式推送
    * @param sftp
    * @param localFile 本地文件的绝对路径
    * @param destPath 目标文件的绝对目录路径
    * @param destFileName 目标文件名称
    * @param newFileName 重命名后的名称
    * @throws ServiceException
    */
    public boolean upload(String localFile, String destPath,String destFileName,String newFileName) throws ServiceException {
    boolean result = false;
    String destFile = destPath;
    String newFile = "";
    try {
    //创建远程路径
    createRemotedir(destPath);
    //拼接远程路径+文件名
    destFile = FileUtil.setDirectoryFile(destPath, destFileName);
    sftp.put(localFile,destFile,ChannelSftp.OVERWRITE);
    //新文件名不为空,拼接重命名后的新文件,并重新命名
    if(!StringUtil.isEmpty(newFileName)){
    newFile = FileUtil.setDirectoryFile(destPath, newFileName);
    sftp.rename(destFile, newFile);
    }
    result =true;
    } catch (Exception e) {
    LOGGER.error(localFile+" upload to "+destFile+" exception "+e.getMessage(), e);
    throw new ServiceException(localFile+" upload to "+destFile+" exception "+e.getMessage(), e);
    }
    return result;
    }

    /**
    * 使用sftp下载文件,若本地存储路径下存在与下载重名的文件,忽略这个文件
    * @param remotePath 服务器上源文件的路径, 必须是目录
    * @param savePath 下载后文件的存储路径, 必须是目录
    * @param remoteFileName 服务器上的文件名称
    * @throws ServiceException
    */
    public boolean download(String remotePath, String savePath,String remoteFileName) throws ServiceException {
    boolean result = false;
    try {
    //切换到远程对应remotePath目录下
    sftp.cd(remotePath);
    //创建本地目录savePath
    createLocalDir(savePath);
    File localFile = new File(FileUtil.setDirectoryFile(savePath, remoteFileName));
    // savePath路径下已有文件与下载文件重名, 忽略这个文件
    if (localFile.exists() && localFile.isFile()) {
    return true;
    }
    //下载远程文件到savePath,文件名称为remoteFile
    sftp.get(remoteFileName, localFile+"");
    result = true;
    } catch (Exception e) {
    LOGGER.error(remotePath+SEPARATOR+remoteFileName+" download to "+savePath+" exception "+e.getMessage(), e);
    throw new ServiceException(remotePath+SEPARATOR+remoteFileName+" download to "+savePath+" exception " +e.getMessage(), e);
    }
    return result;
    }

    /**
    * sftp下载目标服务器上remotePath目录下所有指定的文件.<br/>
    * 若本地存储路径下存在与下载重名的文件,忽略这个文件.<br/>
    * @param remotePath 服务器上源文件的路径, 必须是目录
    * @param savePath 文件下载到本地存储的路径,必须是目录
    * @param fileList 指定的要下载的文件名列表
    * @throws ServiceException
    */
    public boolean downloadFileList(String remotePath, String savePath,List<String> fileList) throws ServiceException {
    boolean result = false;
    try {
    sftp.cd(remotePath);
    String localFile = "";
    for (String srcFile : fileList) {
    try {
    localFile = FileUtil.setDirectoryFile(savePath, savePath);
    File file = new File(localFile);
    // savePath路径下已有文件与下载文件重名, 忽略这个文件
    if (file.exists() && file.isFile()) {
    continue;
    }
    sftp.get(srcFile, localFile);
    } catch (Exception e) {
    LOGGER.error(remotePath + SEPARATOR + srcFile+ " download to " + localFile + " exception "+e.getMessage(), e);
    }
    }
    result = true;
    } catch (Exception e) {
    LOGGER.error(remotePath + " download to " + savePath + " exception "+e.getMessage(), e);
    throw new ServiceException(remotePath + " download to " + savePath + " exception "+e.getMessage(), e);
    }
    return result;
    }

    /**
    * 删除文件
    * @param dirPath 要删除文件所在目录
    * @param file 要删除的文件
    * @param sftp
    * @throws SftpException
    */
    public boolean delete(String dirPath, String file) throws SftpException {
    sftp.cd(SEPARATOR);
    String now = sftp.pwd();
    sftp.cd(dirPath);
    sftp.rm(file);
    sftp.cd(now);
    return true;
    }

    /**
    * 删除文件
    * @param filePath 要删除文件的路径
    * @param sftp
    * @throws SftpException
    */
    public boolean delete(String filePath) throws SftpException {
    sftp.cd(SEPARATOR);
    String now = sftp.pwd();
    sftp.cd(getDirectory(filePath));
    sftp.rm(getFileName(filePath));
    sftp.cd(now);
    return true;
    }


    /**
    * @功能描述 从路径中抽取文件名
    * @param path 路径
    * @return 返回文件名
    */
    private String getFileName(String path) {
    try {
    if (path.contains("\")) {
    return path.substring(path.lastIndexOf("\") + 1);
    }else if(path.contains("/")){
    return path.substring(path.lastIndexOf("/") + 1);
    }else{
    return path;
    }
    } catch (Exception e) {
    return null;
    }
    }
    /**
    * @功能描述 从路径中抽取目录
    * @param path 路径
    * @return 返回目录
    */
    private String getDirectory(String filePath) {
    try {
    if (filePath.contains("\")) {
    return filePath.substring(0, filePath.lastIndexOf("\") + 1);
    }
    return new File(filePath).getPath().substring(0, filePath.lastIndexOf("/") + 1);
    } catch (Exception e) {
    return null;
    }
    }
    /**
    * 获取remotePath路径下以regex格式指定的文件列表,传""或者null默认获取所有文件
    * @param remotePath sftp服务器上的目录
    * @param regex 需要匹配的文件名,
    * @return 获取的文件列表
    * @throws SftpException
    */
    @SuppressWarnings("unchecked")
    public List<String> listFiles(String remotePath, String regex) throws SftpException {
    List<String> fileList = new ArrayList<String>();
    try{
    // 如果remotePath不是目录则会抛出异常
    sftp.cd(remotePath);
    if ("".equals(regex) || regex == null) {
    regex = "*";
    }
    Vector<LsEntry> sftpFiles = sftp.ls(regex);
    String fileName = null;
    for (LsEntry lsEntry : sftpFiles) {
    fileName = lsEntry.getFilename();
    fileList.add(fileName);
    }
    }catch (Exception e) {
    LOGGER.error("get file list from path :"+remotePath + " exception "+e.getMessage(), e);
    fileList = null;
    }
    return fileList;
    }

    /**
    * 根据用户名,主机ip,端口获取一个Session对象
    * @param jsch jsch对象
    * @param host 主机ip
    * @param username 用户名
    * @param port 端口
    * @return
    * @throws Exception
    */
    private Session createSession(JSch jsch, String host, String username,int port) throws Exception {
    Session session = null;
    if (port <= 0) {
    // 连接服务器,采用默认端口
    session = jsch.getSession(username, host);
    } else {
    // 采用指定的端口连接服务器
    session = jsch.getSession(username, host, port);
    }
    // 如果服务器连接不上,则抛出异常
    if (session == null) {
    throw new Exception(host + "session is null");
    }
    // 设置第一次登陆的时候提示,可选值:(ask | yes | no)
    session.setConfig("StrictHostKeyChecking", "no");
    return session;
    }

    /**
    * 循环创建远程路径
    * @param destDirPath
    * @throws SftpException
    */
    private void createRemotedir(String destDirPath) throws SftpException {
    //设置根路径
    sftp.cd(SEPARATOR);
    String[] folders = destDirPath.split(SEPARATOR);
    for (String folder : folders) {
    if (folder.length() > 0) {
    try {
    // 如果folder不存在,则会报错,此时捕获异常并创建folder路径
    sftp.cd(folder);
    } catch (SftpException e) {
    sftp.mkdir(folder);
    sftp.cd(folder);
    }
    }
    }
    }
    /**
    * 创建本地路径
    * @param savePath 本地文件路径
    * @return
    * @throws Exception
    */
    private File createLocalDir(String savePath) throws Exception {
    File localPath = new File(savePath);
    if (!localPath.exists() && !localPath.isFile()) {
    if (!localPath.mkdirs()) {
    throw new Exception(localPath + " directory can not create. ");
    }
    }
    return localPath;
    }




    /**
    * Disconnect with server
    * 断开sftp连接
    */
    public void disconnect() {
    try {
    if (sftp != null) {
    sftp.quit();
    }
    if (sftp != null) {
    sftp.disconnect();
    }
    if (session != null) {
    session.disconnect();
    }
    } catch (Exception e) {
    sftp = null;
    session = null;
    }
    }

    @Override
    public void close() throws Exception {
    sftp.disconnect();
    }

    }

  • 相关阅读:
    LabVIEW-水仙花数
    NRF51822自学笔记(一) 流水灯
    机器学习第四次作业
    4.K均值算法--应用
    机器学习第三次
    机器学习第二次专业
    算符优先分析
    自下而上语法分析
    递归下降语法分析
    LL(1)文法的判断,递归下降分析程序
  • 原文地址:https://www.cnblogs.com/pinghengxing/p/11043674.html
Copyright © 2020-2023  润新知