Profile
Back to NewsBack
GitHub Trending 7 min
Reader Mode
fclairamb/ftpserver: Golang based autonomous FTP server with SFTP, S3, Dropbox, and Google Drive connectors.

fclairamb/ftpserver: Golang based autonomous FTP server with SFTP, S3, Dropbox, and Google Drive connectors.

13 hours ago

Golang FTP Server

Build</a> Docker images</a> Docker test</a> Docker pulls</a> Go Reference</a>

A gateway between old-school FTP devices (cameras, scanners, printers, legacy software...) and modern cloud based file systems.

It is built on top of ftpserverlib, for which it is the reference implementation, and of the afero Fs interface.

Supported backends

| Backend | fs value | Documentation | | --- | --- | --- | | Local disk | os | below | | Amazon S3 and S3-compatible storages (MinIO, Ceph...), through afero-s3 | s3 | fs/s3 | | Google Cloud Storage, through afero's gcsfs | gcs | fs/gcs | | Dropbox, through afero-dropbox | dropbox | fs/dropbox | | Google Drive, through afero-gdrive | gdrive | fs/gdrive | | SFTP, through afero's sftpfs | sftp | fs/sftp | | Email (each uploaded file is sent as an attachment), thanks to @x-way | mail | fs/mail | | Telegram (each uploaded file is sent to a chat), thanks to @slayer | telegram | fs/telegram | | Local disk with users authenticated by Keycloak | keycloak | fs/keycloak |

On top of any backend, an access can be made read-only, share its backend instance between sessions, write through a local temporary directory or log every file access (see fs/README.md).

Features

FTP protocol support is brought by ftpserverlib:

  • Uploading and downloading files, with resume support (REST)
  • Directory listing (LIST, NLST, MLST/MLSD)
  • File and directory deletion and renaming, symbolic links (SITE SYMLINK, on the local disk backend)
  • Passive (PASV, EPSV) and active (PORT, EPRT) transfers, IPv6 support (RFC 2428)
  • Explicit TLS (AUTH TLS, PROT) and implicit TLS
  • File modification time (MDTM) and size (SIZE)
  • File hashing (HASH, XCRC, MD5, XSHA256...), when enabled
  • Small memory footprint
On top of it, this server provides:
  • Multiple users, each one with their own backend
  • Hashed passwords (bcrypt, md5crypt, sha256crypt, sha512crypt), plain-text passwords can be hashed automatically
  • Authentication through an HTTP webhook
  • Configuration hot-reload (SIGHUP) and graceful shutdown (SIGTERM)

Getting started

Download it

Fetch a binary for your platform from the latest release and run it. Archives are named ftpserver--- and are provided for Linux, macOS and Windows on amd64 and arm64, plus armv6/armv7 for Linux (Raspberry Pi and friends).

Build it

go install github.com/fclairamb/ftpserver@latest
ftpserver

Run it with docker

Images are available on Docker Hub and on the GitHub container registry (ghcr.io/fclairamb/ftpserver) for linux/amd64, linux/arm64, linux/arm/v6 and linux/arm/v7:

  • latest, : minimal image, containing only the binary
  • alpine, -alpine: image based on alpine, if you need a shell
The image runs from the /app directory, which is where it reads (or creates) its ftpserver.json config file. The default config file serves the /tmp directory.
# Creating a directory
mkdir -p files

Starting the sample FTP server

docker run --rm -d -p 2121-2130:2121-2130 -v $(pwd)/files:/tmp -v $(pwd):/app fclairamb/ftpserver

Run it with docker compose

# docker-compose.yml
services:
  ftpserver:
    image: fclairamb/ftpserver
    ports:
      - '2121-2130:2121-2130'
    volumes:
      - ./files:/tmp
      - .:/app
docker compose up -d

Test it

# Create some file
head -c 1048576 /dev/urandom > file.bin

Upload it to the server

curl -v -T file.bin ftp://test:test@localhost:2121/

Download it back

curl ftp://test:test@localhost:2121/file.bin -o file2.bin

Compare it

cmp file.bin file2.bin

Usage

ftpserver [-conf <file>] [-conf-only]
  • -conf : config file to use. When not specified, ftpserver.json is used, and created if it doesn't exist.
  • -conf-only: only create the config file (if it doesn't exist) and check it can be loaded, then exit.
Signals:
  • SIGHUP reloads the config file.
  • SIGTERM stops accepting new connections and waits up to one minute for the connected clients to leave.

Configuration

The configuration is a JSON file, described by a JSON schema. Referencing it with the $schema property gives you auto-completion and validation in most editors. If you start the server without any config file, a sample one is created with a single test/test user serving /tmp.

{
  "$schema": "https://raw.githubusercontent.com/fclairamb/ftpserver/main/config-schema.json",
  "version": 1,
  "listen_address": "0.0.0.0:2121",
  "passive_transfer_port_range": {
    "start": 2122,
    "end": 2130
  },
  "accesses": [
    {
      "user": "test",
      "pass": "test",
      "fs": "os",
      "params": {
        "basePath": "/tmp"
      }
    },
    {
      "user": "s3",
      "pass": "$2a$10$jG7tuqIlcUDMl1m1Ytj1TunU7pk.ko8lj3nOGzZvkIeU/BsfPVBra",
      "fs": "s3",
      "read_only": true,
      "params": {
        "bucket": "my-bucket",
        "region": "eu-west-1"
      }
    }
  ]
}

More samples are available in config/samples.

General options

| Property | Description | Default | | --- | --- | --- | | listen_address | Address to listen on | 0.0.0.0:2121 | | public_host | Public IP address to advertise for passive transfers, when the server is behind a NAT gateway. Can be overridden with the PUBLIC_HOST environment variable | | | passive_transfer_port_range | Ports to use for passive transfers (start, end) | any port | | max_clients | Maximum number of simultaneous clients, 0 means no limit | 0 | | idle_timeout | Duration after which idle connections are closed (e.g. 5m), a negative value disables it | 15m | | tls | TLS certificate files: {"server_cert": {"cert": "cert.pem", "key": "key.pem"}} | | | tls_required | ClearOrEncrypted, MandatoryEncryption (explicit TLS required) or ImplicitEncryption (implicit FTPS) | ClearOrEncrypted | | hash_plaintext_passwords | Replace the plain-text passwords of the config file by their bcrypt hash | false | | extensions.enable_hash | Enable the HASH command and its variants | false | | logging.ftp_exchanges | Log all the FTP commands and responses | false | | logging.file_accesses | Log all the file accesses | false | | logging.file | Also write the logs to this file | | | accesses | The users and their backend, see below | | | accesses_webhook | Authenticate users with a webhook instead of the accesses, see below | |

Accesses

Each access defines a user and the backend serving their files:

| Property | Description | | --- | --- | | user | User name | | pass | Password: plain-text or hashed ($2a$/$2b$/$2y$ bcrypt, $1$ md5crypt, $5$ sha256crypt, $6$ sha512crypt). The anonymous user with a * password accepts any password | | fs | Backend to use (see supported backends) | | params | Backend parameters, as strings. See each backend's documentation | | read_only, shared, sync_and_delete, logging | Common options, see fs/README.md |

Accesses are evaluated in their declaration order. A hashed password can be generated with htpasswd -nbBC 10 "" 'my-password' | cut -d: -f2, or by enabling hash_plaintext_passwords.

Local disk

The os backend serves a local directory, defined by the basePath parameter. Environment variables ($NAME) in the path are replaced by their value.

{
  "user": "test",
  "pass": "test",
  "fs": "os",
  "params": {
    "basePath": "/srv/ftp/$FTP_DIR"
  }
}

Webhook authentication

When accesses_webhook is defined, the accesses of the config file are ignored: for each login, the user and password are posted as JSON ({"user": "...", "pass": "..."}) to the webhook. It must reply with a 200 status code and an access object, any other status code refuses the login.

{
  "accesses_webhook": {
    "url": "https://auth.example.com/ftp",
    "headers": {
      "Authorization": "Bearer my-secret-token"
    },
    "timeout": "5s"
  },
  "accesses": []
}

The timeout defaults to 30s.

TLS

You can generate a self-signed TLS key pair with the following command:

openssl req -new -newkey rsa:4096 -x509 -sha256 -days 365 -nodes -out cert.pem -keyout key.pem

Contributing

See CONTRIBUTING.md.

License

MIT

Chat with me