reolink-alpine-vsftpd-server
Small and flexible Docker image with vsftpd server modified for Reolink legacy cameras using PROT C
6.1K
Small and flexible docker image with vsftpd server modified with optional data channel encryption (default YES for security, but set NO for legacy Reolink cameras compatibility using PROT C instead of PROT P). Fork of https://github.com/delfer/docker-alpine-ftp-server
The original image forces full encryption on both control channel (login) and data channel (file uploads). Many older Reolink cameras support explicit FTPS (AUTH TLS on port 21) so the username/password is encrypted, but they never send the PROT P command and always use PROT C (or no PROT at all). This means the video upload data channel stays in clear text.
The original image rejects these cameras because it requires PROT P on every data connection.
FORCE_LOCAL_DATA_SSL (default YES)
YES = original strict behaviour (both login and video upload fully encrypted)NO = relaxed data channel (login remains encrypted via TLS, but video uploads are allowed in clear text with PROT C)This creates a mixed-use scenario on a single server:
docker run -d \
-p "21:21" \
-p 21000-21100:21000-21100 \
-e USERS="camera1|changeme123" \
-e ADDRESS=ftp.example.com \
joeyjojojr/reolink-alpine-vsftpd-server:latest
With FTPS + relaxed data channel (for older Reolink cameras):
docker run -d \
--name vsftpd \
-p "21:21" \
-p 21000-21100:21000-21100 \
-e USERS="camera1|changeme123|/home/ftp/camera1|2000|2000 camera2|changeme123|/home/ftp/camera2|2001|2001" \
-e ADDRESS=ftp.example.com \
-e TLS_CERT="/etc/letsencrypt/live/ftp.example.com/fullchain.pem" \
-e TLS_KEY="/etc/letsencrypt/live/ftp.example.com/privkey.pem" \
-e FORCE_LOCAL_DATA_SSL="NO" \
-v /path/to/ftp/data:/home/ftp \
-v /path/to/letsencrypt:/etc/letsencrypt:ro \
--restart unless-stopped \
joeyjojojr/reolink-alpine-vsftpd-server:latest
Environment variables:
USERS - space and | separated list (optional, default: alpineftp|alpineftp)
name1|password1|[folder1][|uid1][|gid1] name2|password2|[folder2][|uid2][|gid2]ADDRESS - external address to which clients can connect for passive ports (optional, should resolve to ftp server ip address)MIN_PORT - minimum port number to be used for passive connections (optional, default 21000)MAX_PORT - maximum port number to be used for passive connections (optional, default 21100 in this fork)TLS_CERT - path to fullchain.pem (enables FTPS)TLS_KEY - path to privkey.pem (enables FTPS)FORCE_LOCAL_DATA_SSL - YES (default - require PROT P / encrypted data) or NO (allow PROT C / clear data for mixed-use scenario with legacy cameras)user|password foo|bar|/home/foouser|password|/home/user/dir|10000user|password|/home/user/dir|10000|10000user|password||10000user|password||10000|82 : add to an existing group (www-data)Use Certbot with DNS-01 challenge (no port 80 needed) for automatic renewal.
docker compose example (recommended full setup):
services:
ftp:
image: joeyjojojr/reolink-alpine-vsftpd-server:latest
container_name: vsftpd
ports:
- "21:21"
- "21000-21100:21000-21100"
environment:
USERS: >-
camera1|changeme123|/home/ftp/camera1|2000|2000
camera2|changeme123|/home/ftp/camera2|2001|2001
camera3|changeme123|/home/ftp/camera3|2002|2002
ADDRESS: "ftp.example.com"
TLS_CERT: "/etc/letsencrypt/live/ftp.example.com/fullchain.pem"
TLS_KEY: "/etc/letsencrypt/live/ftp.example.com/privkey.pem"
FORCE_LOCAL_DATA_SSL: "NO"
MIN_PORT: "21000"
MAX_PORT: "21100"
volumes:
- /path/to/ftp/data:/home/ftp
- /path/to/certbot:/etc/letsencrypt:ro
restart: unless-stopped
depends_on:
- certbot
certbot:
image: certbot/dns-cloudflare:latest
container_name: certbot
entrypoint: /bin/sh
command:
- -c
- |
trap 'exit 0' TERM INT
echo "Certbot renewal loop started"
while true; do
certbot renew \
--non-interactive \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
--dns-cloudflare-propagation-seconds 60 \
--agree-tos \
--email [email protected] \
--preferred-challenges dns-01 || echo "Renew failed - will retry"
find /etc/letsencrypt/live /etc/letsencrypt/archive -type d -exec chmod 755 {} \;
find /etc/letsencrypt/live /etc/letsencrypt/archive -type f -exec chmod 644 {} \;
echo "Sleeping 12 hours..."
sleep 12h &
wait $!
done
volumes:
- /path/to/certbot:/etc/letsencrypt
- /path/to/certbot/cloudflare.ini:/etc/letsencrypt/cloudflare.ini:ro
restart: unless-stopped
Place your Cloudflare API token in /path/to/certbot/cloudflare.ini (format: dns_cloudflare_api_token = YOUR_TOKEN)
Reolink provides detailed documentation on FTP/FTPS support and setup:
Pull the image: docker pull joeyjojojr/reolink-alpine-vsftpd-server:latest
Thanks to the original delfer/docker-alpine-ftp-server project!
Content type
Image
Digest
sha256:493f410fd…
Size
4.1 MB
Last updated
4 months ago
docker pull joeyjojojr/reolink-alpine-vsftpd-server