Skip to main content

Node.js FTP Server

Shenzhen, China

I want to use the nodeftp package to set up an FTP server for my IP camera.

Setup

Create a folder /opt/ftpd and initialize your project:

mkdir -p /opt/ftpd/ftproot && cd /opt/ftpd
npm init

Dependencies

Then install the following dependencies:

npm install dotenv
npm install fs
npm install ftpd
npm install path

Environment Variables

Now we need a few environment variables to define our server parameter and user authentication credentials:

nano .env

Add the following variables - make sure to change the host IP to the IP address of your server:

IP = '192.168.2.111'
PORT = 9876
USER = ftpuser
PWD = password

Server Script

Now to the server itself - create a file index.js:

const ftpd = require('ftpd')
const fs = require('fs')
const path = require('path')

require('dotenv').config()

var keyFile
var certFile
var server

// use the IP and PORT from the .env file or default to localhost:21
var options = {
  host: process.env.IP || '127.0.0.1',
  port: process.env.PORT || 21,
  tls: null,
}

// Check if SSL KEY / CERT are provided ELSE start without SSL support
if (process.env.KEY_FILE && process.env.CERT_FILE) {
  console.log('Running as FTPS server')
  if (process.env.KEY_FILE.charAt(0) !== '/') {
    keyFile = path.join(__dirname, process.env.KEY_FILE)
  }
  if (process.env.CERT_FILE.charAt(0) !== '/') {
    certFile = path.join(__dirname, process.env.CERT_FILE)
  }
  options.tls = {
    key: fs.readFileSync(keyFile),
    cert: fs.readFileSync(certFile),
    ca: !process.env.CA_FILES
      ? null
      : process.env.CA_FILES.split(':').map(function (f) {
          return fs.readFileSync(f)
        }),
  }
} else {
  console.log()
  console.log('###### To run as FTPS server, #####')
  console.log('### set "KEY_FILE", "CERT_FILE" ###')
  console.log('###### or "CA_FILES" env vars. ####')
  console.log()
}

// get ftp root directory listing
server = new ftpd.FtpServer(options.host, {
  getInitialCwd: function () {
    return '/ftproot'
  },
  getRoot: function () {
    return process.cwd()
  },
  pasvPortRangeStart: 1025,
  pasvPortRangeEnd: 1050,
  tlsOptions: options.tls,
  allowUnauthorizedTls: true,
  useWriteFile: false,
  useReadFile: false,
  uploadMaxSlurpSize: 7000, // N/A unless 'useWriteFile' is true.
  allowedCommands: [
    'XMKD',
    'AUTH',
    'TLS',
    'SSL',
    'USER',
    'PASS',
    'PWD',
    'OPTS',
    'TYPE',
    'PORT',
    'PASV',
    'LIST',
    'CWD',
    'MKD',
    'SIZE',
    'STOR',
    'MDTM',
    'DELE',
    'QUIT',
  ],
})

server.on('error', function (error) {
  console.log('FTP Server error:', error)
})

// verify user and password from .env file
server.on('client:connected', function (connection) {
  var username = null

  console.log('client connected: ' + connection.remoteAddress)

  connection.on('command:user', function (user, success, failure) {
    if (user) {
      username = process.env.USER
      success()
    } else {
      failure()
    }
  })

  connection.on('command:pass', function (pass, success, failure) {
    if (process.env.PWD) {
      success(username)
    } else {
      failure()
    }
  })
})

server.debugging = 4
server.listen(options.port)
console.log('Listening on port ' + options.port)

Run your Server

You can start the server with node index.js or by creating an npm start script and connect to your server using an FTP Client like Filezilla:

ftpd Nodejs FTP Server

Adding Encryption /doesn't work

mkdir certs && cd certs
openssl req -new -x509 -sha256 -newkey rsa:2048 -nodes -keyout key.pem -days 365 -out cert.pem

Add the following variables - make sure to change the host IP to the IP address of your server:

IP = '192.168.2.111'
PORT = 9876
USER = ftpuser
PWD = password
CERT_FILE = './certs/cert.pem'
KEY_FILE = './certs/key.pem'

I am getting deprecation warnings and could not get my client to connect:

npm start

> node-file-server@1.0.0 start /opt/ftpd
> node index.js

Running as FTPS server
Listening on port 9876
client connected: undefined
Accepted a new client connection
<::ffff:192.168.2.112> >> 220 FTP server (nodeftpd) ready
<::ffff:192.168.2.112> << AUTH TLS
<::ffff:192.168.2.112> FTP command: AUTH TLS
<::ffff:192.168.2.112> >> 234 Honored
<::ffff:192.168.2.112> Establishing secure connection...
(node:44550) [DEP0064] DeprecationWarning: tls.createSecurePair() is deprecated. Please use tls.TLSSocket instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
/opt/ftpd/node_modules/ftpd/lib/starttls.js:62
    var verifyError = (pair._ssl || pair.ssl).verifyError();
                                              ^

TypeError: Cannot read property 'verifyError' of undefined
    at SecurePair.<anonymous> (/opt/ftpd/node_modules/ftpd/lib/starttls.js:62:47)
    at SecurePair.emit (events.js:315:20)
    at TLSSocket.<anonymous> (tls.js:322:46)
    at Object.onceWrapper (events.js:421:28)
    at TLSSocket.emit (events.js:315:20)
    at TLSSocket._finishInit (_tls_wrap.js:932:8)
    at TLSWrap.onhandshakedone (_tls_wrap.js:149:9)
    at DuplexSocket.ondata (internal/js_stream_socket.js:77:22)
    at DuplexSocket.emit (events.js:315:20)
    at addChunk (internal/streams/readable.js:309:12)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! node-file-server@1.0.0 start: `node index.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the node-file-server@1.0.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /root/.npm/_logs/2021-02-03T10_59_40_679Z-debug.log