Simple container for FTP+TLS+authentication
Go to file
paspo 25eb14f93b
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Vulnerability Scan / Daily Vulnerability Scan (push) Successful in 16s
cached trivy db
2024-12-02 16:45:42 +01:00
.gitea/workflows cached trivy db 2024-12-02 16:45:42 +01:00
rootfs healthcheck timeout 2024-02-20 19:02:28 +01:00
.drone.yml packages upgrade 2024-06-16 16:30:25 +02:00
Dockerfile packages upgrade 2024-06-16 16:30:25 +02:00
LICENSE Initial commit 2019-05-17 22:22:28 +00:00
manifest.tmpl packages upgrade 2024-06-16 16:30:25 +02:00
README.md sql auth and healthcheck 2024-02-11 21:09:40 +01:00

docker-ftps

Build Status

Simple container for FTP+TLS+authentication

Supported architectures:

Architecture Available Tag
x86-64 <version tag>-linux-amd64
arm64 <version tag>-linux-arm64
armhf -

build

docker build . -t docker.asperti.com/paspo/ftps

run

docker run -d --name my-ftps \
  -p 21:21 -p 20:20 -p 50000-50500:50000-50500 \
  -e "MASQUERADE=ftp.mydomain.com" \
  -v "$PWD/auth:/auth" -v "$PWD/ftpdata:/home" \
  -v "$PWD/certs:/certs" \
  docker.asperti.com/paspo/ftps

The MASQUERADE parameter is the only required one. You can use an IP address (which is discouraged) or a DNS name. You must provide valid certificates for TLS; if you use Lets'Encrypt, you can modify like this:

docker run -d --name my-ftps \
  -p 21:21 -p 20:20 -p 50000-50500:50000-50500 \
  -e "MASQUERADE=ftp.mydomain.com" \
  -v "$PWD/auth:/auth" -v "$PWD/ftpdata:/home" \
  -v "/etc/letsencrypt/live/ftp.mydomain.com:/certs" \
  docker.asperti.com/paspo/ftps

docker-compose (external certificate)

version: "3"
services:

  ftps-server:
    image: docker.asperti.com/paspo/ftps
    restart: always
    ports:
      - "21:21"
      - "20:20"
      - "21210-21220:21210-21220"
    volumes:
      - "/srv/ftps/auth:/auth"
      - "/srv/ftps/conf:/etc/proftpd/custom.conf.d:ro"
      - "/srv/ftps/data:/home"
      - "/etc/letsencrypt:/certs:ro"
    environment:
      - MASQUERADE=ftp.mydomain.com
      - PASSIVEPORTS_START=21210
      - PASSIVEPORTS_END=21220
      - MAXCLIENTS=500
      - MAXCLIENTSPERHOST=100
      - TLS_CERT=/certs/live/ftp.mydomain.com/cert.pem
      - TLS_KEY=/certs/live/ftp.mydomain.com/privkey.pem
      - TLS_CHAIN=/certs/live/ftp.mydomain.com/chain.pem

docker-compose (using internal acme.sh)

version: "3"
services:

  ftps-server:
    image: docker.asperti.com/paspo/ftps
    restart: always
    ports:
      - "21:21"
      - "20:20"
      - "21210-21220:21210-21220"
    volumes:
      - "/srv/ftps/auth:/auth"
      - "/srv/ftps/conf:/etc/proftpd/custom.conf.d:ro"
      - "/srv/ftps/data:/home"
      - "/srv/ftps/acme:/acme"
    environment:
      - MASQUERADE=ftp.mydomain.com
      - PASSIVEPORTS_START=21210
      - PASSIVEPORTS_END=21220
      - MAXCLIENTS=500
      - MAXCLIENTSPERHOST=100
      - ENABLE_ACME=1 # "1" will enable, anything else means external cert
      - ACME_SERVER=letsencrypt # optional
      - ACME_EMAIL=myemail@gmail.com # used by letsencrypt
      - ACME_DNS=dns_ovh # see below
      - OVH_END_POINT=ovh-eu
      - OVH_AK=abc123abc123abc1 # application key
      - OVH_AS=abc123abc123abc1abc123abc123abc1 # application secret
      - OVH_CK=abc123abc123abc1abc123abc123abc1 # consumer key

The rationale behind the acme.sh alternative

You normally use an external letsencrypt client to obtain the certificate and then pass it to the docker container. In some cases, you can't use an external acme client and/or you can't do HTTP-01 auth. The included acme.sh client will help you to setup DNS-01 auth and in keeping the cert updated. Please check here for supported dns providers. Each provider will use different environment variables, you have to add these variables to the container's environment.

OVH

A quick way to create required credentials for OVH:

  • login to your OVH accuont
  • paste in your browser an URL like the following one:
https://api.ovh.com/createToken/?GET=/domain/zone/mydomain.com/*&POST=/domain/zone/mydomain.com/*&PUT=/domain/zone/mydomain.com/*&GET=/domain/zone/mydomain.com&DELETE=/domain/zone/mydomain.com/record/*

This will create some credentials that'll allow management only for that domain (mydomain.com).

passive ports

If you want to change the passive ports range (which by default is 50000-50050), you can do so via environment variables (PASSIVEPORTS_START and PASSIVEPORTS_END). In any case, you also have to enable a matching range of exposed ports.

notes

Please note that you have to restart the container (or send sighup to proftpd) whenever the certificate is renewed. We mount the complete letsencrypt directory because the in live/ftp.mydomain.com we have symlinks to the actual live certificates and in the container these will refer to non-existant files. Also FTP active mode doesn't work until you configure networking as "host".

users management

To change/set a password, do like this (replace "paolo" with the correct username):

docker exec -ti my-ftps ftpasswd --passwd --name=paolo --uid=1000 --home=/home/paolo --sha512 --shell=/bin/false --file=/auth/passwd

You also have to create and chown the user's home folder.

sql db for user authentication

It is possible to use a sqlite db for user authentication, just add SQLITE_AUTH=1 to the environment:

version: "3"
services:
  ftps-server:
    image: docker.asperti.com/paspo/ftps
    restart: always
    ports:
      - "21:21"
      - "20:20"
      - "21210-21220:21210-21220"
    volumes:
      - "/srv/ftps/auth:/auth"
      - "/srv/ftps/conf:/etc/proftpd/custom.conf.d:ro"
      - "/srv/ftps/data:/home"
      - "/etc/letsencrypt:/certs:ro"
    environment:
      - SQLITE_AUTH=1
      - MASQUERADE=ftp.mydomain.com
      - PASSIVEPORTS_START=21210
      - PASSIVEPORTS_END=21220
      - MAXCLIENTS=500
      - MAXCLIENTSPERHOST=100
      - TLS_CERT=/certs/live/ftp.mydomain.com/cert.pem
      - TLS_KEY=/certs/live/ftp.mydomain.com/privkey.pem
      - TLS_CHAIN=/certs/live/ftp.mydomain.com/chain.pem

Now, instead of using /auth/passwd, proftpd is using /auth/ftpd.db. To create a new user, you must now update this db.

To create a new user:

docker exec -ti my-ftps sqlite3 sqlite3 /auth/ftpd.db <<EOF
INSERT OR IGNORE INTO users (userid,passwd,uid,gid,homedir,shell) VALUES ('new_user','',1000,1000,'/home/new_user','/bin/false');
INSERT OR IGNORE INTO groups (groupname,gid,members) VALUES ('new_user',1000,'new_user');
EOF

To update a password:

PASSWD_SHA=$(echo -n ChangeThisPass | mkpasswd -m sha512)
docker exec -ti my-ftps sqlite3 sqlite3 /auth/ftpd.db <<EOF
UPDATE users SET passwd='$PASSWD_SHA' WHERE userid='new_user';
EOF