Skip to main content

FTP in front of AWS S3, powered by asyncio and aiohttp

Project description

aioftps3 CircleCI Maintainability Test Coverage

FTP in front of AWS S3, using asyncio, and aiohttp. Only a subset of the FTP protocol is supported, with implicit TLS and PASV mode; connections will fail otherwise.


pip install aioftps3

An SSL key and certificate must be present $HOME/ssl.key and $HOME/ssl.crt respectively. To create a self-signed certificate, you can use openssl.

openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj /CN=selfsigned \
    -keyout $HOME/ssl.key \
    -out $HOME/ssl.crt


python -m aioftps3.server_main


Configuration is through environment variables

Varaiable Description Example
AWS_AUTH_MECHANISM How requests to AWS are authenticated. Can be secret_access_key or ecs_role. If ecs_role it is expected that the server runs in an ECS container. secret_access_key
AWS_ACCESS_KEY_ID The ID of the AWS access key, if AWS_AUTH_MECHANISM is secret_access_key. ommitted
AWS_SECRET_ACCESS_KEY The secret part of the AWS access key, if AWS_AUTH_MECHANISM is secret_access_key ommitted
AWS_S3_BUCKET_REGION The region of the S3 bucket that stores the files. eu-west-1
AWS_S3_BUCKET_HOST The hostname used to communicate with S3.
AWS_S3_BUCKET_NAME The name of the bucket files are stored in. my-bucket-name
AWS_S3_BUCKET_DIR_SUFFIX The suffix of the keys created in order to simulate a directory. Must start with a forward slash, but does not need to be longer. /
FTP_USERS__i__LOGIN For i any integer, the username of an FTP user that can login. my-user
FTP_USERS__i__PASSWORD_HASHED For i any integer, the hash, as generated by, of the password of an FTP user that can login, using the salt in FTP_USERS__i__PASSWORD_SALT ommitted
FTP_COMMAND_PORT The port that the server listens on for command connections. 8021
FTP_DATA_PORTS_FIRST The first data port in the range for PASV mode data transfers. 4001
FTP_DATA_PORTS_COUNT The number of ports used after FTP_DATA_PORTS_FIRST. 30
FTP_DATA_CIDR_TO_DOMAINS__i__CIDR For i any integer, a CIDR range used to match the IP of incoming command connections. If a match is found, the IP of the corresponding domain or IP address in FTP_DATA_CIDR_TO_DOMAINS__i__DOMAIN is returned to the client in response to PASV mode requests. Some clients will respond to FTP_DATA_CIDR_TO_DOMAINS__i__DOMAIN being by making PASV mode data connections to the same IP as the original command connection, but not all.
HEALTHCHECK_PORT The port the server listens on for healthcheck requests, such as from an AWS network load balancer. 8022

Advanced usage

The code in aioftps3.server_main satisfies a very particular use case, which may not be useful to most. However, the bulk of the code can be used for other cases: you will have to write your own aioftps3.server_main-equivalent, using the functions aioftps3.server.on_client_connect and aioftps3.server_socket.server. For example, you could

  • Store credentials, appropriately hashed, differently, .e.g. in a database.
  • Have the credentials hashed differently.
  • Allow/deny PASV mode data connections based on some condition.

See the source of aioftps3.server_main for how these functions can be used.

Creating a password and salt

python ./

Running tests

Certificates must be created, and Minio, which emulates S3 locally, must be started

./ && ./

and then to run the tests themselves.


Features / Design / Limitations

  • Can upload files bigger than 2G: uses multipart upload under the hood.

  • Does not store uploading files in memory before uploading them to S3: i.e. it is effectively a streaming upload. However, it's not completely streaming: each part of multipart upload is stored in memory before it begins to transfer to S3, in order to be able to hash its content and determine its length.

  • For uploading files, hashes are computed incrementally as data comes in in order to not block the event loop just before uploads to S3.

  • As few dependencies as is reasonable: aiohttp and its dependencies. Boto 3 is not used.

  • May not behave well if upload to the server is faster than its upload to S3.

  • There is some locking to deal with the same files being operated on concurrently. However...

  • .... it does nothing to deal with eventual consistency of S3, and so some operations may appear to not have an immediate effect.

Building and running locally

docker build -t ftps-s3 . && \
docker run --rm -p 8021-8042:8021-8042 \
  -e AWS_AUTH_MECHANISM=secret_access_key \
  -e AWS_ACCESS_KEY_ID=ommitted \
  -e AWS_SECRET_ACCESS_KEY=ommitted \
  -e AWS_S3_BUCKET_REGION=eu-west-1 \
  -e \
  -e AWS_S3_BUCKET_NAME=my-bucket-name \
  -e FTP_USERS__1__LOGIN=user \
  -e FTP_USERS__1__PASSWORD_HASHED=ommitted \
  -e FTP_USERS__1__PASSWORD_SALT=ommitted \
  -e FTP_COMMAND_PORT=8021 \

Building and pushing to Quay

docker build -t ftps-s3 . && \
docker tag ftps-s3:latest && \
docker push

Building and pushing healthcheck application to Quay

docker build -t ftps-s3-healthcheck . -f Dockerfile-healthcheck && \
docker tag ftps-s3-healthcheck:latest && \
docker push

Building and pushing Minio, used for testing, to Quay

docker build -t ftps-s3-minio . -f Dockerfile-minio && \
docker tag ftps-s3-minio:latest && \
docker push

Project details

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aioftps3-0.0.6.tar.gz (4.5 kB view hashes)

Uploaded Source

Built Distribution

aioftps3-0.0.6-py3-none-any.whl (11.5 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page