FTPS (TLS/SSL) from Ruby on Rails App

Viewed 5216

I have an FTP server which only accepts connections through running FTPS (explicit FTP over TLS). I need to be able to connect to this using a Ruby on Rails app.

Does anybody know of a method to do this? I have tried the Net::FTP library but this does not appear to support FTPS connections.

7 Answers

Since Ruby 2.4, TLS over FTP has been available with Net::FTP... this has caused gems like double-bag-ftps to become archived and all your google searches to yield outdated answers.

If you can do explicit FTP over TLS (Connects to FTP normally, then issues a command AUTH TLS to switch to TLS Mode), then great... that should be able to use Ruby's Net::FTP out of the box by just passing {ssl: true} in the options.

Implicit FTP over TLS (runs over TLS from the get-go) does not work out of the box, however, and you must override Net::FTP's connection method to establish an SSL socket and then optionally send commands to the FTP server.

Inidka K posted a Github Gist, but since those are bad form (can go stale), I've posted my version that works against a ShareFile Implicit FTP setup (which seems to only support Implicit FTP):

require 'net/ftp'

class ImplicitFtp < Net::FTP
  FTP_PORT = 990

  def connect(host, port = FTP_PORT)
    synchronize do
      @host = host
      @bare_sock = open_socket(host, port)
      begin
        ssl_sock = start_tls_session(Socket.tcp(host, port))
        @sock = BufferedSSLSocket.new(ssl_sock, read_timeout: @read_timeout)
        voidresp
        if @private_data_connection
          voidcmd("PBSZ 0")
          voidcmd("PROT P")
        end
      rescue OpenSSL::SSL::SSLError, Net::OpenTimeout
        @sock.close
        raise
      end
    end
  end
end

Then, in your code:

ftp_options = {
  port:    990,
  ssl:      true,
  debug_mode: true, # If you want to see what's going on
  username: FTP_USER,
  password: FTP_PASS
}
ftp = ImplicitFtp.open(FTP_HOST, ftp_options)
puts ftp.list
ftp.close

Support for implicit FTPS was merged into ruby/net-ftp in January 2022 (in this PR). If you want to make use of this straight away, you can include the latest version directly in your Gemfile:

gem "net-ftp", github: "ruby/net-ftp", branch: "master"

Then you just need:

options = {
  ssl: true,
  port: 990,
  implicit_ftps: true,
  username: "your-user",
  password: "*********",
  debug_mode: true
}

Net::FTP.open("yourhost.com", options) do |ftp|
  ftp.list.map{ |f| puts f }
end
Related