8 Commits

Author SHA1 Message Date
Thomas Leister
7dff0209f5 Fixes #14: Prevents directory listing in subdirectories
This commit prevents directory listings in every directory level,
not just on root level ("upload/").

In earlier versions, an attacker could get a list of past uploads
if he ever received a valid file download path, removed the file name and
descendet in parent directories.
2020-01-08 19:38:58 +01:00
Thomas Leister
65ca295289 Updates README.md 2019-08-07 20:08:32 +02:00
Thomas Leister
3e06fb8d3d Adds ipv6 support to doc and examples 2019-05-15 17:41:18 +02:00
Thomas Leister
6e287dbe56 Fixes Nginx config mistake in README.md 2019-05-11 12:13:07 +02:00
Thomas Leister
37234aadd5 Fixes #10 - Adds CORS OPTIONS support 2019-05-10 19:16:37 +02:00
Thomas Leister
75a5bf2316 Merge branch 'master' of github.com:ThomasLeister/prosody-filer 2019-04-29 20:54:45 +02:00
Thomas Leister
669e4c89e0 Readme.de: Add section about Ejabberd, general improvements 2019-04-29 20:54:28 +02:00
Thomas Leister
6a2e8cde1f Adds Apache config 2019-02-07 17:40:38 +01:00
3 changed files with 139 additions and 39 deletions

168
README.md
View File

@@ -2,7 +2,9 @@
A simple file server for handling XMPP http_upload requests. This server is meant to be used with the Prosody [mod_http_upload_external](https://modules.prosody.im/mod_http_upload_external.html) module.
*(This module can also be used with future versions of Ejabberd: https://github.com/processone/ejabberd/commit/fface33d54f24c777dbec96fda6bd00e665327fe)*
**Despite the name, this server is also compatible with Ejabberd and Ejabberd's http_upload module!**
---
## Why should I use this server?
@@ -18,7 +20,7 @@ A simple file server for handling XMPP http_upload requests. This server is mean
> The GC will free the memory at some point, but the OS may still report that Prosody is using that memory due to the way the libc allocator works.
> Most long lived processes behave this way (only increasing RAM, rarely decreasing)."
* This server works without any script interpreters or additional dependencies. It is delivered as a binary.
* Go is very good at serving HTTP requests.
* Go is very good at serving HTTP requests and "made for this task".
## Download
@@ -34,19 +36,25 @@ To compile the server, you need a full Golang development environment. This can
Then checkout this repo:
go get github.com/ThomasLeister/prosody-filer
```sh
go get github.com/ThomasLeister/prosody-filer
```
and switch to the new directory:
cd $GOPATH/src/github.com/ThomasLeister/prosody-filer
```sh
cd $GOPATH/src/github.com/ThomasLeister/prosody-filer
```
The application can now be build:
### Build static binary
./build.sh
```sh
### Build static binary
./build.sh
### OR regular Go build
go build main.go
### OR regular Go build
go build main.go
```
## Set up / configuration
@@ -56,11 +64,15 @@ The application can now be build:
Create a new user for Prosody Filer to run as:
adduser --disabled-login --disabled-password prosody-filer
```sh
adduser --disabled-login --disabled-password prosody-filer
```
Switch to the new user:
su - prosody-filer
```sh
su - prosody-filer
```
Copy
@@ -69,12 +81,18 @@ Copy
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 **dis**abled and ```mod_http_upload_external``` is **en**abled! Then configure the external upload module:
```
```lua
http_upload_external_base_url = "https://uploads.myserver.tld/upload/"
http_upload_external_secret = "mysecret"
http_upload_external_file_size_limit = 50000000 -- 50 MB
@@ -84,13 +102,26 @@ 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:
```yaml
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:
```
### IP address and port to listen to, e.g. "127.0.0.1:5050"
listenport = "127.0.0.1:5050"
```toml
### IP address and port to listen to, e.g. "[::]:5050"
listenport = "[::1]:5050"
### Secret (must match the one in prosody.conf.lua!)
secret = "mysecret"
@@ -142,24 +173,36 @@ Done! Prosody Filer is now listening on the specified port and waiting for reque
Create a new config file ```/etc/nginx/sites-available/uploads.myserver.tld```:
server {
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
```nginx
server {
listen 80;
listen [::]:80;
listen 443 ssl;
listen [::]:443 ssl;
server_name uploads.myserver.tld;
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;
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;
client_max_body_size 50m;
location /upload/ {
proxy_pass http://127.0.0.1:5050/upload/;
proxy_request_buffering off;
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:
@@ -173,24 +216,41 @@ Reload Nginx:
systemctl reload nginx
#### Configuration for letting nginx serve the uploaded files
#### Alternative configuration for letting Nginx serve the uploaded files
*(not officially supported - user contribution!)*
```nginx
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name xmppserver.tld;
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;
client_max_body_size 51m;
client_body_buffer_size 51m;
try_files $uri $uri/ @prosodyfiler;
}
location @prosodyfiler {
proxy_pass http://127.0.0.1:5050;
proxy_pass http://[::1]:5050;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
@@ -199,11 +259,51 @@ server {
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:

View File

@@ -2,8 +2,8 @@
### Server configuration
### (rename this file to "config.toml"!)
### IP address and port to listen to, e.g. "127.0.0.1:5050"
listenport = "127.0.0.1:5050"
### IP address and port to listen to, e.g. "[::]:5050" to listen to ipv6 and ipv4 addresses
listenport = "[::]:5050"
### Secret (must match the one in prosody.conf.lua!)
secret = "mysecret"

View File

@@ -48,7 +48,6 @@ func addCORSheaders(w http.ResponseWriter) {
w.Header().Set("Access-Control-Max-Age", "7200")
}
/*
* Request handler
* Is activated when a clients requests the file, file information or an upload
@@ -67,7 +66,7 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
log.Println("Failed to parse URL query params:", err)
}
fileStorePath := strings.TrimPrefix(u.Path, "/" + conf.UploadSubDir)
fileStorePath := strings.TrimPrefix(u.Path, "/"+conf.UploadSubDir)
// Add CORS headers
addCORSheaders(w)
@@ -137,7 +136,8 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", contentType)
} else if r.Method == "GET" {
contentType := mime.TypeByExtension(filepath.Ext(fileStorePath))
if fileStorePath == "" {
if f, err := os.Stat(conf.Storedir + fileStorePath); err != nil || f.IsDir() {
log.Println("Directory listing forbidden!")
http.Error(w, "403 Forbidden", 403)
return
}