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