Giter VIP home page Giter VIP logo

prosody-filer's Introduction

Prosody Filer

A simple file server for handling XMPP http_upload requests. This server is meant to be used with the Prosody mod_http_upload_external module.

Despite the name, this server is also compatible with Ejabberd and Ejabberd's http_upload module!


Why should I use this server?

Originally this software was written to circumvent memory limitations / issues with the Prosody-internal http_upload implementation at the time. These limitations do not exist, anymore. Still this software can be used with Ejabberd and Prosody as an alternative to the internal http_upload servers.

Download

If you are using regular x86_64 Linux, you can download a finished binary for your system on the release page. No need to compile this application yourself.

Build (optional)

If you're using something different than a x64 Linux, you need to compile this application yourself.

To compile the server, you need a full Golang development environment. This can be set up quickly: https://golang.org/doc/install#install

Then checkout this repo:

go install github.com/ThomasLeister/prosody-filer

and switch to the new directory:

cd $GOPATH/src/github.com/ThomasLeister/prosody-filer

The application can now be build:

### Build static binary
./build.sh

### OR regular Go build
go build prosody-filer.go

Set up / configuration

Setup Prosody Filer environment

Create a new user for Prosody Filer to run as:

adduser --disabled-login --disabled-password prosody-filer

Switch to the new user:

su - prosody-filer

Copy

  • the binary prosody-filer and
  • config config.example.toml

to /home/prosody-filer/. Rename the configuration to config.toml.

Make sure the prosody-filer binary is executable:

chmod u+x prosody-filer

Configure Prosody

Back in your root shell make sure mod_http_upload is disabled and mod_http_upload_external is enabled! Then configure the external upload module:

Component "uploads.myserver.tld" "http_upload_external"
    http_upload_external_base_url = "https://uploads.myserver.tld/upload/"
    http_upload_external_secret = "mysecret"
    http_upload_external_file_size_limit = 50000000 -- 50 MB

Restart Prosody when you are finished:

systemctl restart prosody

Alternative: Configure Ejabberd

Although this tool is named after Prosody, it can be used with Ejabberd, too! Make sure you have a Ejabberd configuration similar to this:

  mod_http_upload:
    put_url: "https://uploads.@HOST@/upload"
    external_secret: "mysecret"
    max_size: 52428800

Configure Prosody Filer

Prosody Filer configuration is done via the config.toml file in TOML syntax. There's not much to be configured. The most important piece is the secret setting, which needs to match the secret defined in your mod_http_upload_external settings!

### IP address and port to listen to, e.g. "[::]:5050"
listenport      = "[::1]:5050"

### Secret (must match the one in prosody.conf.lua!)
secret          = "mysecret"

### Where to store the uploaded files
storeDir        = "./upload/"

### Subdirectory for HTTP upload / download requests (usually "upload/")
uploadSubDir    = "upload/"

In addition to that, make sure that the nginx user or group can read the files uploaded via prosody-filer if you want to have them served by nginx directly.

Docker usage

To build container:

docker build . -t prosody-filer:latest

To run container use:

docker run -it --rm -v $PWD/config.example.toml:/config.toml prosody-filer -config /config.toml

Systemd service file

Create a new Systemd service file: /etc/systemd/system/prosody-filer.service

[Unit]
Description=Prosody file upload server

[Service]
Type=simple
ExecStart=/home/prosody-filer/prosody-filer
Restart=always
WorkingDirectory=/home/prosody-filer
User=prosody-filer
Group=prosody-filer
# Group=nginx  # if the files should get served by nginx directly:

[Install]
WantedBy=multi-user.target

Reload the service definitions, enable the service and start it:

systemctl daemon-reload
systemctl enable prosody-filer
systemctl start prosody-filer

Done! Prosody Filer is now listening on the specified port and waiting for requests.

FreeBSD service file

  • First, install go and build the static binary with go install github.com/ThomasLeister/prosody-filer. The binary should be in ~/go/bin/prosody-filer.
  • Create an user with pw user add -n prosodyfiler -c 'Prosody Filer' -d /home/prosodyfiler -m -s /usr/sbin/nologin and copy the binary into /home/prosodyfiler/.
  • Put this rc script in /etc/rc.d/prosody-filer:
#!/bin/sh

# PROVIDE: prosody_filer
# REQUIRE: FILESYSTEMS networking
# KEYWORDS: upload

. /etc/rc.subr

name="prosody_filer"
program_name="prosody-filer"
title="Prosody-Filer"
rcvar=prosody_filer_enable
prosody_filer_user="prosodyfiler"
prosody_filer_group="prosodyfiler"
prosody_filer_chdir="/home/prosodyfiler/"

pidfile="/home/prosodyfiler/${program_name}.pid"
required_files="/home/prosodyfiler/config.toml"
exec_path="/home/prosodyfiler/${program_name}"
output_file="/var/log/${program_name}.log"

command="/usr/sbin/daemon"
command_args="-r -t ${title} -o ${output_file} -P ${pidfile} -f ${exec_path}"

load_rc_config $name
  • Execute sysrc prosody_filer_enable=YES

You can now start the service via service prosody_filer start and check it's log via /var/log/prosody-filer.log.

Configure Nginx

Create a new config file /etc/nginx/sites-available/uploads.myserver.tld:

server {
    listen 80;
    listen [::]:80;
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name uploads.myserver.tld;

    ssl_certificate /etc/letsencrypt/live/uploads.myserver.tld/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/uploads.myserver.tld/privkey.pem;

    client_max_body_size 50m;

    location /upload/ {
        if ( $request_method = OPTIONS ) {
                add_header Access-Control-Allow-Origin '*';
                add_header Access-Control-Allow-Methods 'PUT, GET, OPTIONS, HEAD';
                add_header Access-Control-Allow-Headers 'Authorization, Content-Type';
                add_header Access-Control-Allow-Credentials 'true';
                add_header Content-Length 0;
                add_header Content-Type text/plain;
                return 200;
        }

        proxy_pass http://[::]:5050/upload/;
        proxy_request_buffering off;
    }
}

Enable the new config:

ln -s /etc/nginx/sites-available/uploads.myserver.tld /etc/nginx/sites-enabled/

Check Nginx config:

nginx -t

Reload Nginx:

systemctl reload nginx

Alternative configuration for letting Nginx serve the uploaded files

(not officially supported - user contribution!)

server {
    listen 80;
    listen [::]:80;
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name uploads.myserver.tld;

    ssl_certificate /etc/letsencrypt/live/uploads.myserver.tld/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/uploads.myserver.tld/privkey.pem;

    location /upload/ {
        if ( $request_method = OPTIONS ) {
                add_header Access-Control-Allow-Origin '*';
                add_header Access-Control-Allow-Methods 'PUT, GET, OPTIONS, HEAD';
                add_header Access-Control-Allow-Headers 'Authorization, Content-Type';
                add_header Access-Control-Allow-Credentials 'true';
                add_header Content-Length 0;
                add_header Content-Type text/plain;
                return 200;
        }

        root /home/prosody-filer;
        autoindex off;
        client_max_body_size 51m;
        client_body_buffer_size 51m;
        try_files $uri $uri/ @prosodyfiler;
    }
    location @prosodyfiler {
        proxy_pass http://[::1]:5050;
        proxy_buffering off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host:$server_port;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-For $remote_addr;
    }
}

apache2 configuration (alternative to Nginx)

(This configuration was provided by a user and has never been tested by the author of Prosody Filer. It might be outdated and might not work anymore)

<VirtualHost *:80>
    ServerName upload.example.eu
    RedirectPermanent / https://upload.example.eu/
</VirtualHost>

<VirtualHost *:443>
    ServerName upload.example.eu
    SSLEngine on

    SSLCertificateFile "Path to the ca file"
    SSLCertificateKeyFile "Path to the key file"

    Header always set Public-Key-Pins: ''
    Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"
    H2Direct on

    <Location /upload>
        Header always set Access-Control-Allow-Origin "*"
        Header always set Access-Control-Allow-Headers "Content-Type"
        Header always set Access-Control-Allow-Methods "OPTIONS, PUT, GET"

        RewriteEngine On

        RewriteCond %{REQUEST_METHOD} OPTIONS
        RewriteRule ^(.*)$ $1 [R=200,L]
    </Location>

    SSLProxyEngine on

    ProxyPreserveHost On
    ProxyRequests Off
    ProxyPass / http://localhost:5050/
    ProxyPassReverse / http://localhost:5050/
</VirtualHost>

Automatic purge

Prosody Filer has no immediate knowlegde over all the stored files and the time they were uploaded, since no database exists for that. Also Prosody is not capable to do auto deletion if mod_http_upload_external is used. Therefore the suggested way of purging the uploads directory is to execute a purge command via a cron job:

@daily    find /home/prosody-filer/upload/ -mindepth 1 -type d -mtime +28 -print0 | xargs -0 -- rm -rf

This will delete uploads older than 28 days.

Check if it works

Get the log via

journalctl -f -u prosody-filer

If your XMPP clients uploads or downloads any file, there should be some log messages on the screen.

prosody-filer's People

Contributors

cbix avatar eerielili avatar kousu avatar lykos153 avatar rabioli avatar sysvinit avatar thomasleister avatar weiss avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

prosody-filer's Issues

H2Direct not recognized

I'm using the user provided Apache config and it complains about H2Direct on not being recognized, so I comment it out and Apache works, but when I try to upload something I get error 405 (method not allowed) on Gajim and Conversations.

File size limit

What is the difference between
client_max_body_size 50m; in /etc/nginx/sites-available/uploads.myserver.tld
and
http_upload_external_file_size_limit = 50000000 -- 50 MB in /etc/prosody/prosody.cfg.lua

Should be change both when we want to change the file size limit?

Automatic purge

Hi,

in your readme file, you say to create a job cron.
I however have an issue with the purge command suggested.
I tried find /home/prosody-filer/upload -maxdepth 0 -type d just to see what I get. And I get /home/prosody-filer/upload
Shouldn't it be find /home/prosody-filer/upload/* -maxdepth 0 -type d ? Like this, I get results...

Automatic purge : remove message

I'm sorry, perhaps this request is just not possible:
when an upload is delete with the purge script, the message that included the link to this file still exist. That means that if someone didn't receive the file before it was deleted (meaning it is in the client storage), this person sees a message with a link to the file (at least on gajim), but when he/she clicks on it, he/she gets a 404 error, as the file doesn't exist anymore.

Is that something that could be fixed easily?

By the way, thanks for the great work, prosody-filter works like a charm!

create a new release

I was trying to package 1.0.2 for Alpine. I got:

prosody-filer.go:25:2: no required module provides package github.com/BurntSushi/toml: go.mod file not found in current directory or any parent directory; see 'go help modules'

Which seems to be fixed in the latest develop head:

#27

Please cut a new release so that this is fixed.

Version v1.0.0 response invalid mac

Hi,

all file uploads are repsonse with invalid mac:

Feb 01 02:09:24 prosody-filer[34172]: MAC sent:  596rogbetoh3hteb
Feb 01 02:09:24 prosody-filer[34172]: 2019/02/01 02:09:24 fileStorePath: upload/zko4b3j kbrv/1.png
Feb 01 02:09:24 prosody-filer[34172]: 2019/02/01 02:09:24 ContentLength: 26856
Feb 01 02:09:24 prosody-filer[34172]: 2019/02/01 02:09:24 Invalid MAC.

With the last release candidat are not this problem. Give it a reason for this problem?

SomeThing wrong with HMAC

Yo, ThomasLeister,
Thanks for your work!
Works like a charm,
But i have some troubles
with HMAC verify on Prosody 0.12,
with all my clients (Conversation, Dino)
Filer returns error code with message from here:

if a["v"] == nil {
log.Println("Error: No HMAC attached to URL.")
http.Error(w, "409 Conflict", 409)
return
}
fmt.Println("MAC sent: ", a["v"][0])

So, i just rewrited a little to bypass this behavior
Commented the above, and rewrite this line

if hmac.Equal([]byte(macString), []byte(a["v"][0])) {

To this:

if a != nil || a == nil {

It works, but i'm a bit confused,
Because I want to do all the things right))

So, maybe @ThomasLeister could give me some advice?)

Implement a way to recieve a token at runtime rather than hardcoded

I keep my configs public and with gobs of services I've used, this is the first that seems to only allow hardcoded values in its file. Either allowing to be read from a file, an environment variable (could be used with systemd), or UNIX command would help clear this up.

Invalid MAC

Hi, thanks for this useful piece of Software. I have a small problem and I would like to ask for your review and help:

xmpp-filer_1              | 2020/02/29 08:20:29 Incoming request: PUT /_xmpp/upload/57bb1d75-2bd7-46b2-8360-55301d168628/RECORDING_20200227_193520573.m4a?v=d88af5b00418ba437dd063be44699bb24b04a9bd9770406e5a6fd7e13bf9f457
xmpp-filer_1              | MAC sent:  d88af5b00418ba437dd063be44699bb24b04a9bd9770406e5a6fd7e13bf9f457
xmpp-filer_1              | 2020/02/29 08:20:29 fileStorePath: 57bb1d75-2bd7-46b2-8360-55301d168628/RECORDING_20200227_193520573.m4a
xmpp-filer_1              | 2020/02/29 08:20:29 ContentLength: 36677
xmpp-filer_1              | 2020/02/29 08:20:29 Invalid MAC.

It runs on this dockerized setup together with Prosody 11.2 and the file sent was triggered by a resend Conversations client. It lives behind a Traefik reverse proxy.

It has worked great. And suddenly started having this problem. I upgraded to the latest master of the prosody-filer and tried again. Still having the same problem. Do you have any idea? Thank you!

(btw, I saw the other couple of issues around invalid MACs, but the problems there doesn't seem to apply to my setup)

Feature Request: Use hash of URL as filename

Sadly, OMEMO doesn't hide the filename from the server, despite this meta datum can already contain sensitive information.

The URL contains a random value to prevent listing existing files via brute force, so it has high entropy.
Every request of the file via an XMPP client is done via the URL. So, instead of storing the file in the filesystem by its name, a hash of the URL can be used as name. This prevents the admin (or attackers getting read access to the filesystem) to get potentially sensitive meta data.
As encoding for the hash, I propose base32(hex) without padding to keep it short and safe even for systems with case-insensitive filesystems.

Instead of the full URL, only the path or even just the path info (see PATH_INFO at https://www.php.net/reserved.variables.server).

This could be even extended to use hkdf to derive 2 keys: one for obfuscating the filename and a second one to encrypt the content of the file - in case the uploader didn't use something like OMEMO.

It would be best if OMEMO would not only encrypt the file's content but also hides the file name and the content type. But as it's not the case, this can add an additional layer of protection. It, however, cannot protect against malicious admins as the admin could (a) read the process memory at the time of a file up-/download or (b) could just disable it or use a different software.

A vulnerability has been fixed, but no specific tag denotes the patched version.

Hello, we are a team researching the dependency management mechanism of Golang. During our analysis, we came across your project and noticed that you have fixed a vulnerability (snyk references, CWE: CWE-200, fix commit id: 990373d). However, we observed that you have not tagged the fixing commit or its subsequent commits. As a result, users are unable to obtain the patch version through Go tool ‘go list’.

We kindly request your assistance in addressing this issue. Tagging the fixing commit or its subsequent commits will greatly benefit users who rely on your project and are seeking the patched version to address the vulnerability.

We greatly appreciate your attention to this matter and collaboration in resolving it. Thank you for your time and for your valuable contributions to our research.

Investigate MAC generation / comparison

There seems to be a bug in Prosody filer which leads to wrong MAC calculation and therefore blocks some uploads once in a while. Re-trying to upload the file solves the issue in most cases. Sometimes more that one retry is needed until successful upload.

I could not recreate the error case so far - it seems to be quite random to me.

Maybe this is related to any race conditions / parallel execution / comparison in the code.

Files upload in folder

Hi,

why are files uploaded in a folder inside of /uploads ? WHy are they not directly uploaded in /uploads, without creating subfolders?

Prosody updated info

Hi,

Prosody devs updated the mod_http_upload_external page.
This module should not be added to modules_enabledn but as a component:

Component "upload.example.org" "http_upload_external"
http_upload_external_base_url = "https://your.example.com/upload/service"
http_upload_external_secret = "your shared secret"

So you may want to update your readme file ;)

Error with Group=nginx

I set everything as explained.
I use "Alternative configuration for letting Nginx serve the uploaded files"

In "Systemd service file", if I set Group=nginx I get:
systemd[1]: Failed to start Prosody file upload server.
If I set Group=prosody-filer
Then it works fine...

Verifying sources and/or binaries

It seems that you currently do not sign the git tags or the published binary.
To enable (semi-)automatic updates of prosody-filer in production, it would be nice to have some way to automatically verify that the sources used to build the binary or the downloaded binary itself is indeed still coming from you ;)

Advantage of files served by nginx directly

In the readme file, it is written:
# Group=nginx # if the files should get served by nginx directly:

What would be the advantages over letting the default setting to Group=prosody-filer

Project seems out of date

It´s not possible to build this software by running

$ go build main.go

as there is no main.go file

Changing it to the more correct prosody-filer.go also doesn´t work

$ go build prosody-filer.go
prosody-filer.go:25:2: no required module provides package github.com/BurntSushi/toml: go.mod file not found in current directory or any parent directory; see 'go help modules'

This seems to be ( and I might be wrong, I don´t know much about go) because this software does not use a current golang way of packaging.

In addition the toml parser library this project uses had a big deprecation warning on the top of it´s readme.

If prosody-flyer is not in a usable state, maybe it makes sense to add a notice to the README.

Setting up: info missing

Hi,

Just want to suggest to add in the howto a line explaining that there should be an uploads folder created in /home/prosody-filer and that it should belongs to the prosody-filer user
mkdir /home/prosody-filer/uploads
chown prosody-filer:prosody-filer /home/prosody-filer/uploads

Running bin file

SELinux prevents you from running a system service where the binary is in a user's home directory, or in your case, the root user's home directory.

To fix the problem, copy the binary to a proper directory such as /usr/local/bin and call it from there.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.