Set up a Secure FTP Server for your WQHD Camera using Docker

Update: This tutorial works with both Active (PORT Mode) and Passive (PASV Mode) uploads. If you only need Port Mode please read our tutorial Set up an FTP Server for your WQHD Camera using Docker.

Q: I want to use my cameras FTP service to upload alarm recordings and snapshot series to my Linux Server (Raspberry Pi).

A: The simplest way would be to use your Internet Router as FTP Server. If you want to use a Linux Server like an Raspberry Pi we recommend using the sFTP Service instead of FTP or FTPS. But of course you can also set up your own FTP server instead. I would recommend building an vsftpd Docker image for this task.

Preparing the Docker Image

Start by creating a work directory and your Dockerfile, e.g. :

mkdir -p /opt/vsftpd/ftpuser && cd /opt/vsftpd
nano Dockerfile

And add the following content. Make sure port 20 - 21 as well as 4242 - 4243, are being forwarded to the server. We will need these extra ports for the passive mode - set pasv_min_port and pasv_max_port accordingly in the vsftpd.conf configuration file below.

# Dockerfile for vsftpd on CentOS7
FROM centos:7

MAINTAINER m.polinowski@instar.com

RUN yum -y update; yum -y install which vsftpd net-tools vsftpd-sysvinit; yum clean all

COPY vusers.txt /etc/vsftpd/
RUN db_load -T -t hash -f /etc/vsftpd/vusers.txt /etc/vsftpd/vsftpd-virtual-user.db; rm -v /etc/vsftpd/vusers.txt; \
	chmod 600 /etc/vsftpd/vsftpd-virtual-user.db
COPY vsftpd.conf /etc/vsftpd/
COPY vsftpd.virtual /etc/pam.d/
RUN mkdir -p /home/vftp/ftpuser; chown -R ftp:ftp /home/vftp

EXPOSE 20 21 4242 4243

CMD ["/usr/sbin/vsftpd","-obackground=NO"]

This Dockerfile will take the CentOS as a base image - this can be replaced by any flavour of Enterprise Linux. The next step installs the vsftpd service and creates the FTP User account for us. After that we need to copy the following configuration files into the image - all of the need to be created in the same directory where we placed our Dockerfile:

FTP User Login

vusers.txt

ftpuser
mypassword

This is the user we will have to use to connect to the FTP server - change both the username and password according to your needs.

FTP User Configuration

vsftpd.virtual

#%PAM-1.0
auth       required     pam_userdb.so db=/etc/vsftpd/vsftpd-virtual-user
account    required     pam_userdb.so db=/etc/vsftpd/vsftpd-virtual-user
session    required     pam_loginuid.so

Server Configuration

vsftpd.conf

ftpd_banner=Welcome to the INSTAR FTP service.
# Enforce user logins
anonymous_enable=NO
guest_enable=YES
# Local linux users are allowed to log in
local_enable=YES
# Make sure PORT transfer connections originate from port 20
connect_from_port_20=YES
# Enable PASV mode on ports 4242-4243
pasv_enable=YES
pasv_addr_resolve=YES
pasv_min_port=4242
pasv_max_port=4243
virtual_use_local_privs=YES
# FTP users can create, delete, rename and save files
write_enable=YES
# Default umask for local users
local_umask=022
# Add user
pam_service_name=vsftpd.virtual
user_sub_token=$USER
local_root=/home/vftp/$USER
chroot_local_user=YES
allow_writeable_chroot=YES
hide_ids=YES
# Set network interface
listen=YES
listen_ipv6=NO
# Activate logging of uploads/downloads.
xferlog_enable=YES
xferlog_std_format=YES
# log file in standard ftpd xferlog format
xferlog_file=/var/log/vsftpd.log

Enable TLS Encryption

Starting from the standard configuration file vsftpd.conf from the previous step we now need to add a TLS certificate to our server. If your server runs on a public server you can use a service like Let's Encrypt / Certbot to generated a valid certificate for the domain name of your server. You can use this certificate for your Web as well as your FTP server. Otherwise you can follow our tutorial on how to generate your own CA certificate. This tutorial goes through the steps of generating a valid CA cert for a local domain (provided by an AVM Fritzbox Home Internet Router) and put it to use with an external MQTT Broker. The same certificate can be used here as well.

But in the following steps we will simply generate a self-signed certificate that can be used with an local IP address (instead of a domain name). Of course, this means that we will have to activate the insecure mode in our cameras FTP client. This does not mean that the encryption will be weaker, but the camera will not try to verify if your personal FTP server really is the server the TLS certificate was issued to.

Generate a Private Key and Certificate

Start by creating a private key and a TLS certificate with Openssl. To generate a private key, run:

mkdir /opt/vsftpd/tls
openssl genrsa -out /opt/vsftpd/vsftpd.key

Next, generate a certificate signing request with the command below. You will be asked several questions during this process. Since we are not going to verify the resulting certificate, you can leave most of those fields empty. Just add your FTP servers local IP address when asked for the FQDN (fully qualified domain name), e.g. 192.168.2.111:

openssl req -new -key /opt/vsftpd/vsftpd.key -out /opt/vsftpd/vsftpd.csr

You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:.
State or Province Name (full name) [Some-State]:.
Locality Name (eg, city) []:.
Organization Name (eg, company) [Internet Widgits Pty Ltd]:.
Organizational Unit Name (eg, section) []:.
Common Name (e.g. server FQDN or YOUR name) []:192.168.2.111
Email Address []:.

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Now, generate and sign the certificate which will be valid for 397 days (the maximum allowed length = 13 months) as follows:

openssl x509 -req -days 397 -in /opt/vsftpd/vsftpd.csr -signkey /opt/vsftpd/vsftpd.key -out /opt/vsftpd/vsftpd.pem

And make sure that Docker will be able to use these new files:

chmod 755 /opt/vsftpd/*

Add the Certificate and Key to your Configuration File

vsftpd.conf

ftpd_banner=Welcome to the INSTAR FTP service.
# Enforce user logins
anonymous_enable=NO
guest_enable=YES
# Local linux users are allowed to log in
local_enable=YES
# Make sure PORT transfer connections originate from port 20
connect_from_port_20=YES
# Enable PASV mode on ports 4242-4243
pasv_enable=YES
pasv_addr_resolve=YES
pasv_min_port=4242
pasv_max_port=4243
virtual_use_local_privs=YES
# FTP users can create, delete, rename and save files
write_enable=YES
# Default umask for local users
local_umask=022
# Add user
pam_service_name=vsftpd.virtual
user_sub_token=$USER
local_root=/home/vftp/$USER
chroot_local_user=YES
allow_writeable_chroot=YES
hide_ids=YES
# Set network interface
listen=YES
listen_ipv6=NO
# Add encryption
rsa_cert_file=/etc/vsftpd/vsftpd.pem
rsa_private_key_file=/etc/vsftpd/vsftpd.key
ssl_enable=YES
allow_anon_ssl=NO
force_local_data_ssl=YES
force_local_logins_ssl=YES
ssl_tlsv1=YES
ssl_sslv2=NO
ssl_sslv3=NO
require_ssl_reuse=NO
ssl_ciphers=HIGH
# Activate logging of uploads/downloads.
xferlog_enable=YES
xferlog_std_format=YES
# log file in standard ftpd xferlog format
xferlog_file=/var/log/vsftpd.log

Copy Certificate and Key into your Docker Image

# Dockerfile for vsftpd on CentOS7
FROM centos:7

MAINTAINER m.polinowski@instar.com

RUN yum -y update; yum -y install which vsftpd net-tools vsftpd-sysvinit; yum clean all

COPY vusers.txt /etc/vsftpd/
RUN db_load -T -t hash -f /etc/vsftpd/vusers.txt /etc/vsftpd/vsftpd-virtual-user.db; rm -v /etc/vsftpd/vusers.txt; \
	chmod 600 /etc/vsftpd/vsftpd-virtual-user.db
COPY vsftpd.conf /etc/vsftpd/
COPY vsftpd.virtual /etc/pam.d/
COPY vsftpd.pem /etc/vsftpd/
COPY vsftpd.key /etc/vsftpd/
RUN mkdir -p /home/vftp/ftpuser; chown -R ftp:ftp /home/vftp

EXPOSE 20 21 4242 4243

CMD ["/usr/sbin/vsftpd","-obackground=NO"]

Starting the Docker Container

Build the Image

With those 4 files in place we are now ready to build our Docker image:

docker build -t vsftpd_tls .

Run the Container

Make sure that the FTP user directory exists and can be written to by your Docker user:

mkdir -p /opt/vsftpd/ftpuser
chmod -R 755 /opt/vsftpd/ftpuser/

Make sure that the home directory /opt/vsftpd/ftpuser is set to 777 to prevent the FTP write error ftp 550 Create directory operation failed.

docker run -d \
    --name vsftpd \
    --net=host \
    --privileged \
    --rm \
    -v /opt/vsftpd/ftpuser/:/home/vftp/ftpuser/ \
    vsftpd_tls:latest