Serve a http upload server that uses HMAC for auth

1. Caddy version (caddy version):

v2.6.2 h1:wKoFIxpmOJLGl3QXoo6PNbYvGW4xLEgo32GPBEjWL8o= - official caddy debian repo version

2. How I run Caddy:

systemctl start caddy (using the caddyfile in /etc/caddy/Caddyfile)

a. System environment:

Running Debian 11
prosody-filer v1.0.2
using systemD
System up to date

b. Command:

systemctl start/restart caddy

c. Service/unit/compose file:

# caddy.service
#
# For using Caddy with a config file.
#
# Make sure the ExecStart and ExecReload commands are correct
# for your installation.
#
# See https://caddyserver.com/docs/install for instructions.
#
# WARNING: This service does not use the --resume flag, so if you
# use the API to make changes, they will be overwritten by the
# Caddyfile next time the service is restarted. If you intend to
# use Caddy's API to configure it, add the --resume flag to the
# `caddy run` command or use the caddy-api.service file instead.

[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target

[Service]
Type=notify
User=caddy
Group=caddy
ExecStart=/usr/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /etc/caddy/Caddyfile
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target

d. Caddyfile or JSON config:

I am showing only the relevant bit since I don’t have anything configured that could affect this at as a global setting.
This config half works, since the PUT request is being made but the http server that handles the upload behind the proxy, but it reports the HMAC signature is missing.

upload.domain.tld {
	reverse_proxy localhost:5050
}

3. The problem I’m having:

I am running an upload service using HMAC for authentication, the server I am using for this is prosody-filer, I have to mention that the secret is both correctly set up in the server and the “client”, the thing is that the signature is missing, specially since it seems to connect but its rejected by the upload server.

4. Error messages and/or full log output:

Error message of my http upload server:

2022/12/11 03:36:31 Incoming request: PUT /external/IXlPedub60p9791/20221210_213546818_62b1.jpg?token=14553edeaa1a36243cdbd2504063b04ef62081fc2d1d9dc974a6b1fb161ce79d
2022/12/11 03:36:31 Error: No HMAC attached to URL.

Curl response on the address my service is behind of:

~ ❯❯❯ curl -v domain.tld
*   Trying $SERVER_IP:80...
* Connected to domain.tld ($SERVER_IP) port 80 (#0)
> GET / HTTP/1.1
> Host: domain.tld
> User-Agent: curl/7.86.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 308 Permanent Redirect
< Connection: close
< Location: https://domain.tld/
< Server: Caddy
< Date: Sun, 11 Dec 2022 03:41:25 GMT
< Content-Length: 0
< 
* Closing connection 0

5. What I already tried:

I haven’t tried a lot since I really don’t know how to handle HMAC in caddy and there is not a lot of documentation about it, I saw some plugins and some directive related to load balancing, but nothing completely related to the issue I am having.

However, I did try to adapt the nginx example configuration they provide (they are in the README of the github repo). What I tried didn’t even let me upload which was restricting and handling specific paths like /upload, it didn’t “route”, with the plain reverse proxy it did and that’s why I left it like that, I am looking for guidance and assistance for translating the apache and nginix config files to caddy, since I know caddy a bit but not the other servers.

6. Links to relevant resources:

Http Upload server - https://github.com/ThomasLeister/prosody-filer

You’re just making an HTTP request here, not HTTPS. Caddy is just serving a redirect response. You’d need to use the -L option (L for Location header) to follow the redirect, or just try curl -v https://example.com instead.

HMAC is just “hash-based message authentication code”. It has nothing to do with webservers. It’s just an algorithm for taking some data plus a key, and outputting a hash, and that hash can be verified by something else by doing the same operation with the same data and key to ensure only someone with that key could have done the same operation.

Your config looks fine to me, looking at their nginx example.

I’m seeing no evidence of a problem with Caddy from what you’ve shared. It seems more likely to be a misconfiguration of that app, rather than an issue with Caddy.

2 Likes

Sorry, I was out, and then I went out to test other solutions that also failed.

Ran the command the way you pointed out, and now it shows more meaningful info.

Another thing to take into consideration is that the upload server I am using expects a path for PUT and GET requests for what I understand, this path is set up in my server as /upload/, as such I will show you both curl outputs (that you can test yourself).

I have to mention the same issue is still there even tho everything is supposedly correctly set up. And in addition, sometimes the server seems to duplicate the request and my client shows an error of connection reset.

The only clue I have now is using the handle directive to pass all incoming request to /upload, but I wouldn’t know what to set up there or for all other requests.

~ ❯❯❯ curl -Lv xup.novoa.nagoya
*   Trying [2607:4300:1:c005:0:48:210:0]:80...
* Connected to xup.novoa.nagoya (2607:4300:1:c005:0:48:210:0) port 80 (#0)
> GET / HTTP/1.1
> Host: xup.novoa.nagoya
> User-Agent: curl/7.86.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 308 Permanent Redirect
< Connection: close
< Location: https://xup.novoa.nagoya/
< Server: Caddy
< Date: Tue, 27 Dec 2022 20:11:54 GMT
< Content-Length: 0
< 
* Closing connection 0
* Clear auth, redirects to port from 80 to 443
* Issue another request to this URL: 'https://xup.novoa.nagoya/'
*   Trying [2607:4300:1:c005:0:48:210:0]:443...
* Connected to xup.novoa.nagoya (2607:4300:1:c005:0:48:210:0) port 443 (#1)
* ALPN: offers h2
* ALPN: offers http/1.1
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=xup.novoa.nagoya
*  start date: Dec  9 04:22:12 2022 GMT
*  expire date: Mar  9 04:22:11 2023 GMT
*  subjectAltName: host "xup.novoa.nagoya" matched cert's "xup.novoa.nagoya"
*  issuer: C=US; O=Let's Encrypt; CN=R3
*  SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* h2h3 [:method: GET]
* h2h3 [:path: /]
* h2h3 [:scheme: https]
* h2h3 [:authority: xup.novoa.nagoya]
* h2h3 [user-agent: curl/7.86.0]
* h2h3 [accept: */*]
* Using Stream ID: 1 (easy handle 0x556717f60160)
> GET / HTTP/2
> Host: xup.novoa.nagoya
> user-agent: curl/7.86.0
> accept: */*
> 
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
< HTTP/2 404 
< alt-svc: h3=":443"; ma=2592000
< content-type: text/plain; charset=utf-8
< date: Tue, 27 Dec 2022 20:11:54 GMT
< server: Caddy
< x-content-type-options: nosniff
< content-length: 19
< 
404 page not found
* Connection #1 to host xup.novoa.nagoya left intact

AND the /upload path

~ ❯❯❯ curl -Lv xup.novoa.nagoya/upload
*   Trying [2607:4300:1:c005:0:48:210:0]:80...
* Connected to xup.novoa.nagoya (2607:4300:1:c005:0:48:210:0) port 80 (#0)
> GET /upload HTTP/1.1
> Host: xup.novoa.nagoya
> User-Agent: curl/7.86.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 308 Permanent Redirect
< Connection: close
< Location: https://xup.novoa.nagoya/upload
< Server: Caddy
< Date: Tue, 27 Dec 2022 20:12:00 GMT
< Content-Length: 0
< 
* Closing connection 0
* Clear auth, redirects to port from 80 to 443
* Issue another request to this URL: 'https://xup.novoa.nagoya/upload'
*   Trying [2607:4300:1:c005:0:48:210:0]:443...
* Connected to xup.novoa.nagoya (2607:4300:1:c005:0:48:210:0) port 443 (#1)
* ALPN: offers h2
* ALPN: offers http/1.1
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=xup.novoa.nagoya
*  start date: Dec  9 04:22:12 2022 GMT
*  expire date: Mar  9 04:22:11 2023 GMT
*  subjectAltName: host "xup.novoa.nagoya" matched cert's "xup.novoa.nagoya"
*  issuer: C=US; O=Let's Encrypt; CN=R3
*  SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* h2h3 [:method: GET]
* h2h3 [:path: /upload]
* h2h3 [:scheme: https]
* h2h3 [:authority: xup.novoa.nagoya]
* h2h3 [user-agent: curl/7.86.0]
* h2h3 [accept: */*]
* Using Stream ID: 1 (easy handle 0x55b400a21160)
> GET /upload HTTP/2
> Host: xup.novoa.nagoya
> user-agent: curl/7.86.0
> accept: */*
> 
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
< HTTP/2 301 
< alt-svc: h3=":443"; ma=2592000
< content-type: text/html; charset=utf-8
< date: Tue, 27 Dec 2022 20:12:00 GMT
< location: /upload/
< server: Caddy
< content-length: 43
< 
* Ignoring the response-body
* Connection #1 to host xup.novoa.nagoya left intact
* Issue another request to this URL: 'https://xup.novoa.nagoya/upload/'
* Found bundle for host: 0x55b400a20ce0 [can multiplex]
* Re-using existing connection #1 with host xup.novoa.nagoya
* Connected to xup.novoa.nagoya (2607:4300:1:c005:0:48:210:0) port 443 (#1)
* h2h3 [:method: GET]
* h2h3 [:path: /upload/]
* h2h3 [:scheme: https]
* h2h3 [:authority: xup.novoa.nagoya]
* h2h3 [user-agent: curl/7.86.0]
* h2h3 [accept: */*]
* Using Stream ID: 3 (easy handle 0x55b400a21160)
> GET /upload/ HTTP/2
> Host: xup.novoa.nagoya
> user-agent: curl/7.86.0
> accept: */*
> 
< HTTP/2 403 
< alt-svc: h3=":443"; ma=2592000
< content-type: text/plain; charset=utf-8
< date: Tue, 27 Dec 2022 20:12:00 GMT
< server: Caddy
< x-content-type-options: nosniff
< content-length: 14
< 
403 Forbidden
* Connection #1 to host xup.novoa.nagoya left intact

If the upload server is expecting authentication, then using curl to make the request will of course not be successful either, because you’re not doing anything to authenticate.

Again, I think this is a configuration problem with the upload service.

Looking at the README, are you sure you configured the same secret value on both the clients and the upload service? GitHub - ThomasLeister/prosody-filer: Golang mod_http_upload_external server for Prosody and Ejabberd The secret should be some random text value that you generate (don’t just use the default of secret cause that’s obviously not secret, and therefore insecure).

1 Like

I have set the secret twice once complex then the simplest in an attempt to debug but it is still not working. I tired to copy the config from nginix but the request is not even relayed using it so my best bet still is the simple reverse_proxy but it is still failing to verify the upload.

Hello just for clarification I wanted to let you know that caddy indeed wasn’t the issue, it was some weird way the upload server and the XMPP server were interacting, I had to modify the code of the server to make them compatible.

The simple reverse_proxy I provided was working as expected with no issues. I do wonder now if I could make it more secure without compromising usability.

1 Like

This topic was automatically closed after 30 days. New replies are no longer allowed.