This Prosody module implements XEP-0363 that enables clients to upload files over HTTP on any S3 compatible storage that supports AWS Signature Version 4. Instead of having the XMPP server as the upload (or download) gateway files are uploaded directly to the object store using precomputed short-lived PUT requests.
This is a fork with extra modifications of the original
mod_http_upload_s3
. The
key differences are:
-
A Lua module,
luas3put
, has been written in Rust (with mlua) that wraps the official AWS SDK instead of constructing requests manually on the Lua side. This will allow (eventually) for more extensive support for standard AWS-like operations. -
Endpoint URLs can be customised that enables the use of other S3 compatible hosts. This library has been tested, for example, on both AWS S3 and Cloudfare R2.
-
Instead of checking for
c2s
connections explicitly additional authorised uploaders can be specified (akin to mod_http_file_share). -
You can specify a hostname to serve the files from. This avoids the need for public buckets and you can also use Cloudfront (or any other CDN) with your custom domain deployed in front of your bucket to facilitate caching (and more predictable URLs).
- Compile with
cargo build --release
as usual - Rename the produced library
target/release/libluas3put.so
toluas3put.so
. - Copy both
mod_http_upload_s3.lua
andluas3put.so
into a directory that Prosody can load modules from. - Configure Prosody to use the module (see below)
Steps 1 and 2 can be done automatically on a clean Debian 11 image
by using the included podman script. Rename podman
with docker
in podman-build.sh
if you use docker instead.
You will probably have to adjust the CORS headers on wherever you
are serving the files from to allow GET
, HEAD
, PUT
and
ideally also OPTIONS
for all origins if you want access web-based
XMPP clients to work. Same with the Authorization
, Content-Type
and Content-Length
headers as they are mandated by XEP-0363.
Add a new Component
to the Prosody configuration to handle the uploads.
Component "upload.xmpp.host" "http_upload_s3"
name = "HTTP file upload"
ssl = {
certificate = "/etc/prosody/certs/upload.xmpp.host.crt";
key = "/etc/prosody/certs/upload.xmpp.host.key";
}
-- Bucket region; use auto for Cloudflare
http_upload_s3_region = "eu-west-2";
-- Obvious
http_upload_s3_access_id = "ACCESSID";
-- Obvious
http_upload_s3_secret_key = "SECRET";
-- Optional; will default to S3 if not specified
http_upload_s3_endpoint_url = "https://endpoint.url";
-- Domain to serve files from, otherwise is the
-- public bucket host
http_upload_s3_base_domain = "download.xmpp.host";
-- Name of your storage bucket
http_upload_s3_bucket = "xmppbucket";
-- Directory under bucket to store files; can be empty
http_upload_s3_path = "uploads";
-- Maximum file size; defaults to 100 MiB
http_upload_s3_file_size_limit = 104857600
-- Access list; add jids or hosts that you want to
-- grant upload access in addition to the local hosts
-- specified in Prosody configuration
http_upload_s3_access = {
"filesharingenthusiast@example.net", -- specific JID
"example.org" -- anyone on @example.org
}
Should work with Prosody 0.10+. Tested on 0.11 and 0.12. Should also work with trunk.
- Very rudimentary access control.
- No quotas or user restrictions.
- The lua module modifies
LUA_CPATH
to include the path where the lua module itself is located so that the shared library can be loaded. This is both awkward and might also bring in other modules which might not be desirable. - Error handling on the Rust side could be better and the code could (should?) be neater overall.
- Needs more graceful handling of the unlikely situation of UUID clash.
- The S3 requests are forcefully converted into blocking using
tokio::runtime::Runtime::block_on
. This defeats the asynchronous character of the Rust AWS SDK but at the moment it is unclear how this can be improved. I wish I didn't have to drag in the whole Tokio runtime. Ideas to improve this are welcome!
There are a ton of HTTP upload modules for Prosody and other XMPP servers. If
you want to store files locally you can use either mod_http_file_share
for a
turnkey solution in Lua or mod_http_upload_external
combined with something
like prosody-filer to
offload this to an external component.