kamal icon indicating copy to clipboard operation
kamal copied to clipboard

Support Cloudflare Authenticated Origin Pulls

Open darenas31415 opened this issue 4 months ago • 6 comments

After adding support for custom certificates in #1531, Could you please consider adding support for Cloudflare’s Authenticated Origin Pulls.

This would allow Kamal deployed applications to restrict access to requests coming only through Cloudflare by verifying the client certificate provided by Cloudflare.

This is particularly important when using Cloudflare WAF, as it prevents attackers from bypassing the WAF and accessing the origin directly.

darenas31415 avatar Aug 14 '25 09:08 darenas31415

Cloudflare’s Authenticated Origin Pulls is using mTLS.

This means mTLS support would be needed in kamal-proxy. There is a discussion here about adding it, including a workaround of using another reverse proxy as a kamal accessory in the meantime for mTLS in front of kamal-proxy: https://github.com/basecamp/kamal-proxy/discussions/120

There was also another feature request for that in kamal before: https://github.com/basecamp/kamal/issues/1484

There is support for custom certificates, it would be nice if in a similar manner a trusted_certificate_pem for mTLS could be configured. In the case of Cloudflare Authenticated Origin Pulls this would then be the Cloudflare hostname client certificate.

# config could look like this:
  ssl:
    certificate_pem: CERTIFICATE_PEM # exists
    private_key_pem: PRIVATE_KEY_PEM # exists
    trusted_certificate_pem: TRUSTED_CERTIFCATE_PEM # new
    verify_client: on # allow: on | off | optional | optional_no_ca

Naming similar to e.g. nginx config naming for mTLS, note also the options for different mTLS levels verify_client. The escaped client certificate would usually then be sent upstream from the proxy in an HTTP header like ssl-client-cert.

polarctos avatar Aug 22 '25 15:08 polarctos

A low hanging fruit would be to add security header check first, so that in the absence of the header the proxy would return 404 (same as with invalid host). Then in Cloudflare we can just add the header to all requests to authenticate them.

a3kov avatar Sep 01 '25 02:09 a3kov

A low hanging fruit would be to add security header check first, so that in the absence of the header the proxy would return 404 (same as with invalid host). Then in Cloudflare we can just add the header to all requests to authenticate them.

Such a HTTP header check is already possible to do in the application itself, as HTTP headers are just forwarded, so not necessarily needed to be done by kamal-proxy.

However in the case of mTLS however the TLS connection is terminated by kamal-proxy, and thus this cannot be added just in the application and is a feature that is only possible in the component that terminates the TLS connection.

polarctos avatar Nov 04 '25 17:11 polarctos

Such a HTTP header check is already possible to do in the application itself

We want to pretend the host is not served by the server, unless it's a Cloudflare request. When you get a request for unknown host normally the client receives "not found" response from the proxy itself. Your suggestion won't work, because the mere fact of the app responding "I'm not here" discloses the existence of the host.

a3kov avatar Nov 04 '25 17:11 a3kov

Such a HTTP header check is already possible to do in the application itself

We want to pretend the host is not served by the server, unless it's a Cloudflare request. When you get a request for unknown host normally the client receives "not found" response from the proxy itself. Your suggestion won't work, because the mere fact of the app responding "I'm not here" discloses the existence of the host.

If you really want to go down this path of "hiding" then you can just imitate a common HTTP 404 response from your application and make it look exactly like some common proxies would respond. In the end it is still just a HTTP response that you can fully define, if you generate that from the proxy or the application does not make a difference and can look exactly the same. The existence of the host is already disclosed by answering in the TCP handshake and then in the TLS handshake.

In the mTLS case, instead of "hiding", the TLS handshake would already be terminated if the client certificate is not trusted, thus providing real authentication.

polarctos avatar Nov 04 '25 18:11 polarctos

I don't think "imitating" proxy responses is a good idea at all. You would have to make both the content and the headers match, which would be very fragile and risky. There's no need to build anything like this when we can have this feature in the proxy.

a3kov avatar Nov 04 '25 18:11 a3kov