6

I am using the Net::FTP ruby library to connect to an FTP server and download files. It all works well, but now I need to use an outbound proxy since their firewall whitelists IP addresses, and I am using Heroku to host the site. I'm trying out the new Proximo add-on which looks promising, but I can't get Net::FTP to use it.

I see the following in the Net::FTP docs:

connect(host, port = FTP_PORT)

Establishes an FTP connection to host, optionally overriding the default port. If the environment variable SOCKS_SERVER is set, sets up the connection through a SOCKS proxy. Raises an exception (typically Errno::ECONNREFUSED) if the connection cannot be established.

But trying to set this environment variable, or setting ENV['SOCKS_SERVER'] = proximo_url doesn't work either. Does anyone know how to properly do this? It doesn't have to be a SOCKS proxy, an HTTP proxy will do, but I'm not sure that the Net::FTP library supports this.

... after some research ...

I found that Net::FTP looks for a class called SOCKSSocket, for which I found these docs. But I can't include it. include 'socket' returns false which I'm pretty sure means it's already loaded.

Community
  • 1
  • 1
Erik J
  • 828
  • 9
  • 22

3 Answers3

1

You can use the socksify gem as in combination with eg. Quotaguard. Make sure you use one of the provided static ip's though (and not the DNS hostname) as proxy server though since FTP and loadbalancing can cause troubles.

Socksify::debug = true
proxy_uri = URI(ENV['QUOTAGUARDSTATIC_URL'])
proxy_hostname = proxy_uri.hostname
proxy_port = 1080
TCPSocket::socks_username = proxy_uri.user
TCPSocket::socks_password = proxy_uri.password
Socksify::proxy(proxy_hostname, proxy_port) do |soc| 
       Net::FTP.open(server) do |ftp|
            ftp.passive = true
            ftp.debug_mode = true 
            ftp.login user, password
            ftp.get(export, storelocation)
            ftp.close
       end
end
tvgriek
  • 1,215
  • 9
  • 20
0

This is my take on the problem. The boiler plate:

# Connects to ftp through a socks proxy
#
# TODO: testing, use ssl, capture debugging output, recovery on network failure...
#
# @param ftpHost   [String ] Domain name or ip address of the ftp server
# @param proxyHost [String ] Host name or ip address of the socks proxy
# @param proxyPort [FixNum ] Port number of the socks proxy
# @param proxyUser [String ] User name for connecting to the proxy if needed
# @param proxyPass [String ] Password  for connecting to the proxy if needed
# @param ftpPort   [FixNum ] Ftp port, defaults to 21
# @param ftpUser   [String ] Ftp username
# @param ftpPass   [String ] Ftp password
# @param debug     [Boolean] Whether to turn on debug output of both socksify and Net::FTP, will be sent to STDOUT
# @param passive   [Boolean] Whether to use passive FTP mode
#
def ftp(

        ftpHost:                      ,
        proxyHost:                    ,
        proxyPort:                    ,
        proxyUser: nil                ,
        proxyPass: nil                ,
        ftpPort:   Net::FTP::FTP_PORT ,
        ftpUser:   'anonymous'        ,
        ftpPass:   'anonymous@'       ,
        debug:     false              ,
        passive:   true

    )

    Socksify::debug           = debug
    TCPSocket::socks_username = proxyUser
    TCPSocket::socks_password = proxyPass

    Socksify::proxy( proxyHost, proxyPort ) do |_|

        begin

            # Check whether we got an ip address or a domain name.
            # If needed we'll do dns through the socket to avoid dns leaks.
            #
            Regexp.new( URI::RFC2396_Parser.new.pattern[ :IPV4ADDR ] ) =~ ftpHost  or

                ftpHost = Socksify::resolve( ftpHost )


            ftp            = Net::FTP.new
            ftp.debug_mode = debug
            ftp.passive    = passive

            ftp.connect ftpHost, ftpPort
            ftp.login   ftpUser, ftpPass

            yield ftp

        ensure

            ftp.close

        end

    end

end

Now in your actual application you can just do:

def listRemoteFiles

    connect do |ftp|

        puts ftp.list.inspect  # or other cool functions from Net::FTP

    end

end


def connect &block

    ftp({

        debug:     true            ,
        proxyHost: your.proxy.host ,
        proxyPort: your.proxy.port ,
        ftpHost:   your.ftpHost 

    }, &block )

end

Thanks to tvgriek for pointing in the right direction.

-1

You can use the Proximo wrapper to forward traffic through Proximo. Here are the official instructions: https://devcenter.heroku.com/articles/proximo#forwarding-traffic-through-proximo