3

I am trying to containerise an ftp server NodeJS application. It works fine when I run it with npm but it does not respond when I run it inside of a container.

This is the node app: The src/index.js file

const FtpSvr = require ( 'ftp-srv' );

const hostname = '127.0.0.1';
const port = 21;

const ftpServer = new FtpSvr ({
url:`ftp://${hostname}:${port}`,
anonymous:true
} );

ftpServer.on('login', ({connection, username, password}, resolve, reject) =>
{
    resolve({root : "./"})

    connection.on('STOR', (error, fileName) => {
        console.log("STOR")
    });
});

ftpServer.on ( 'client-error', (connection, context, error) =>
{
    console.log ( `error: ${error}` );
});

ftpServer.listen().then(() => {console.log(`Server running at http://${hostname}:${port}/`);});

my package.json file

{
  "name": "ftp-server",
  "version": "1.0.0",
  "description": "FTP server to receive images from cameras and save them on Azure Blob storage",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node src/index.js"
  },
  "author": "Rakshak",
  "license": "ISC",
  "dependencies": {
    "ftp-srv": "4.3.4"
  }
}

my docker file

FROM node:12

WORKDIR /app

COPY package.json ./

RUN npm install

COPY . .

EXPOSE 20-21
EXPOSE 65500-65515

CMD ["npm", "start"]

I am testing the FTP server using FileZilla. When I run the server using npm start and connect using FileZilla

Connecting to 127.0.0.1:5000...
Status:         Connection established, waiting for welcome message...
Status:         Insecure server, it does not support FTP over TLS.
Status:         Logged in
Status:         Retrieving directory listing...
Status:         Directory listing of "/" successful

I am using this command to build the docker image

 docker build -t rrakshak/ftp-demo .

And I am using this to run the docker

 docker run -p 5000:5000 rrakshak/ftp-demo:latest

This is the message on the FileZilla console

Connecting to 127.0.0.1:5000...
Status:         Connection established, waiting for welcome message...
Error:          Connection closed by server
Error:          Could not connect to server
Status:         Waiting to retry...
Status:         Connecting to 127.0.0.1:5000...
Status:         Connection established, waiting for welcome message...
Error:          Connection closed by server
Error:          Could not connect to server

It looks like when the server is running inside of a container the FileZilla is able to connect but is not receiving the file listing response it expects.

------------Update-----------------------

setting the host to 0.0.0. give me a new set of messages on Filezilla

Status:         Connecting to 127.0.0.1:21...
Status:         Connection established, waiting for welcome message...
Status:         Insecure server, it does not support FTP over TLS.
Status:         Logged in
Status:         Retrieving directory listing...
Command:    PWD
Response:   257 "/"
Command:    TYPE I
Response:   200 Switch to "binary" transfer mode.
Command:    PASV
Response:   502 Command not supported
Command:    PORT 127,0,0,1,231,209
Response:   500 The given address is not yours
Error:          Failed to retrieve directory listing

Why does my app work when I run it in node but not when I containerise it?

DrkStr
  • 1,752
  • 5
  • 38
  • 90
  • [ftp uses more than a single port, and doesn't play nicely with NAT](https://slacksite.com/other/ftp.html) – Matt Oct 20 '20 at 05:39
  • [docker example](https://github.com/metabrainz/docker-anon-ftp) for vsftpd – Matt Oct 20 '20 at 05:43
  • Thanks for that @Matt Am still having trouble making this work. I have updated the Docker file to EXPOSE 20-21 and 65500-65515. I am able to run it at a node app, but it doesn't return a response when running the server from a container. – DrkStr Oct 20 '20 at 06:20
  • So one thing to point out is that you might be setting the bind address only to 127.0.0.1 ... as mentioned, "doesn't play nicely with NAT" --> The request is translated over more than one network interface ( + your server might not be binding to accept traffic on all interfaces - 0.0.0.0) – OneCricketeer Oct 20 '20 at 06:50
  • @OneCricketeer tried setting the host to. 0.0.0.0 to try to accept traffic on all interdaces. Didn't work. Ive updated the question. Does this mean that I can not run a containerised node js FTP server? – DrkStr Oct 20 '20 at 07:18
  • Looks like it did connect, but I'm confused on the `:21...` and `,231,209` parts – OneCricketeer Oct 20 '20 at 07:24
  • Also guessing "The given address is not yours" is a security feature that is protecting you from a possible MITM attack, but wouldn't know where that is enforced – OneCricketeer Oct 20 '20 at 07:26
  • Yeh, am confused about the random ports as well. – DrkStr Oct 20 '20 at 07:27

2 Answers2

2

Listen on 0.0.0.0:5000 in the container, with passive ports defined

const FtpSvr = require ( 'ftp-srv' );
  
const hostname = '0.0.0.0';
const port = 5000;

const ftpServer = new FtpSvr ({
  url: `ftp://${hostname}:${port}`,
  anonymous: true,
  pasv_url: `ftp://${hostname}:${port}`,
  pasv_min: 65500,
  pasv_max: 65515,
});

Build the container as is and then run with the following ports mapped, which can all be used in an ftp connection:

docker run -p 5000:5000 -p 65500-65515:65500-65515 --rm rrakshak/ftp-demo

Gives the response:

$ curl ftp://localhost:5000
-rw-r--r-- 1 1 1          141 Oct 21 01:22 Dockerfile
drwxr-xr-x 1 1 1         4096 Oct 21 01:21 node_modules
-rw-r--r-- 1 1 1        21137 Oct 21 01:21 package-lock.json
-rw-r--r-- 1 1 1           52 Oct 21 01:21 package.json
-rw-r--r-- 1 1 1          660 Oct 21 01:23 server.js
-rw-r--r-- 1 1 1        20287 Oct 21 01:21 yarn.lock

The ftp client must be set to use passive mode.

When an FTP client is in active mode, the FTP server receives a PORT command from the client and creates a new TCP connection from the container back out to the client for data on that PORT.

Due to the Docker port mapping into the container, the source address of this data connection often won't match what the FTP client is using as the initial destination for the FTP server. Similar issues occur when setting up FTP servers behind NAT on classic servers.

Matt
  • 68,711
  • 7
  • 155
  • 158
  • thanks for that. It works on my browser and terminal but not on FileZilla. Getting the usual "Command: PORT 127,0,0,1,234,142 Response: 500 The given address is not yours" response. Any ideal why that might be the case? – DrkStr Oct 21 '20 at 02:04
  • While filezilla is set to use PASV mode? – Matt Oct 21 '20 at 02:07
  • Ah, i didn't know it had a passive mode. Thanks for replying :D – DrkStr Oct 21 '20 at 02:09
0

Try binding to 0.0.0.0. As you are running inside Docker, it will not work to bind against 127.0.0.1, as the request will come from outside (at least, from the perspective of the Docker container).

For hints on troubleshooting these kind of network issues, you can find some ideas in the answer to this related question.

Philipp Claßen
  • 41,306
  • 31
  • 146
  • 239
  • thanks for the reply. When you say "binding to 0.0.0.0" do you mean setting the host in the node application to 0.0.0.0? I've already tried that, it's the update section in the question. It tries to enter passive mode and then give me a message "500 The given address is not yours" – DrkStr Oct 21 '20 at 00:38
  • @DrkStr OK, I see. But I think, these are two separate issues in the end. Being able to connect is the first step, but the FTP error is something else. Searching for it shows that it is happens outside of Docker, too. Perhaps because /etc/hostname is different in Docker then on your host system. It defaults to the container ID. It could be caused by the fact that the internal hostname will not be resolvable on your host system. – Philipp Claßen Oct 21 '20 at 01:04