Caddy as an authentication proxy to secure endpoint

The problem I’m having:

Hi,

I’m trying to synchronize my private calendar with Google Calendar.

My calendar provider uses SOGo but only supports authenticated requests using basic auth, which Google doesn’t support.

My goal is to use Caddy to serve as an authentication proxy: Google → My server → SOGo

I have tried doing this using the reverse_proxy directive, but with no success (domain obscured because my experiments are not secured yet):

cal.my_domain.com {
       reverse_proxy sogo.provider.com
       {
               header_up Authorization "Basic [hash]"
       }
}
curl -v https://cal.my_domain.com/SOGo/dav/user@my_domain.com/Calendar/personal.ics

> GET /SOGo/dav/user@my_domain.com/Calendar/personal.ics HTTP/2
> Host: cal.my_domain.com
> user-agent: curl/7.87.0
> accept: */*

< HTTP/2 502 
< alt-svc: h3=":443"; ma=2592000
< server: Caddy
< content-length: 0
< date: Wed, 19 Apr 2023 13:45:10 GMT

Another option would be to perform a sub-request to the SOGo server with the appropriate Auth header and return the result, but I couldn’t find the directives to either perform network requests, or to run a script without external plugins.

I’d prefer to use only standard Caddy functionality since I installed the Debian apt package and would prefer to not have to reconfigure everything.

Security

Once the above is working, I’d like to secure the request by matching with a long unique string in the request path. Help with this would also be appreciated.

Caddy version:

v2.6.4 h1:2hwYqiRwk1tf3VruhMpLcYTg+11fCdr8S3jhNAdnPy8=

How I installed and ran Caddy:

Installed using apt (official caddy instructions).
Running as system service.

System environment:

Raspbian Linux on Raspberry Pi 4

This implies Caddy couldn’t connect to the upstream. What error do you see in Caddy’s logs? Enable the debug global option for more detailed logs.

Is that an HTTP or an HTTPS upstream? Make sure to configure your upstream correctly. See the docs. reverse_proxy (Caddyfile directive) — Caddy Documentation

Thanks for the reply.

It’s an https upstream. I tried reverse_proxy https://sogo.provider.com {… but got the following error:

Error: adapting config using caddyfile: parsing caddyfile tokens for 'reverse_proxy': Caddyfile:62 - Error during parsing: for now, all proxy upstreams must use the same scheme (transport protocol); expecting 'https://' but got '://'

Here’s the resulting log with the debug option enabled:

{
  "level":"error",
  "ts":1681972159.0650284,
  "logger":"http.log.access.log2",
  "msg":"handled request",
  "request":{
    "remote_ip":"---",
    "remote_port":"50508",
    "proto":"HTTP/2.0",
    "method":"GET",
    "host":"---",
    "uri":"/SOGo/dav/---/Calendar/personal.ics",
    "headers":{
      "User-Agent":[
        "curl/7.87.0"
      ],
      "Accept":[
        "*/*"
      ]
    },
    "tls":{
      "resumed":false,
      "version":772,
      "cipher_suite":4867,
      "proto":"h2",
      "server_name":"---"
    }
  },
  "user_id":"",
  "duration":0.003952798,
  "size":0,
  "status":502,
  "resp_headers":{
    "Server":[
      "Caddy"
    ],
    "Alt-Svc":[
      "h3=\":443\"; ma=2592000"
    ]
  }
}

Update: I managed to get things working. The issue was correctly identified by @francislavoie (thanks!) as a missing https:// in the reverse_proxy directive.

The subsequent parsing error was confusingly due to a newline between the reverse_proxy directive and the {.
Honestly this seems like a bug.

In any case, the following config entry works:

cal.my_domain.com {
       reverse_proxy https://sogo.provider.com {
               header_up Host {upstream_hostport}
               header_up Authorization "Basic [hash]"
       }
}

While this one fails:

cal.my_domain.com {
       reverse_proxy https://sogo.provider.com
       {
               header_up Host {upstream_hostport}
               header_up Authorization "Basic [hash]"
       }
}

Now all I have to do is secure the proxy with a unique string in the path. Most likely using this method with the rewrite directive.

Nope, it’s correct. The { must be on the same line. See Caddyfile Concepts — Caddy Documentation

Fair enough. It was confusing to me coming from other languages.

In any case here’s the final working solution:

cal.example.com {
	handle /long_unique_string {
		rewrite * /SOGo/dav/user@example.com/Calendar/personal.ics
		reverse_proxy https://sogo.provider.com {
			header_up Host {upstream_hostport}
			header_up Authorization "Basic [hash]"
		}
	}
}

Use https://cal.example.com/long_unique_string to perform the request.

1 Like

I agree that the error message was misleading, so I opened a PR to clean that up:

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