Trusted Proxies with Cloudflare - my solution

Been struggling to figure out how to deal with the v2.5 changes to reverse_proxy while using Cloudflare as a cloud proxy service and figured I’d share what’s working for me in hopes that others may find it useful.

in my Caddyfile, i make use of import directive to reference a file:

        reverse_proxy IP:PORT {
                import /authdb/cloudflare-proxies
        }

i run Caddy in a docker container, so i have the /authdb/ volume mounted to the container. on the host itself, i set a cron job to run this script every 30 minutes:

#!/bin/bash

CLOUDFLARE_IPSV4="https://www.cloudflare.com/ips-v4"

FILE_IPV4="/authdb/cloudflare-proxies"

if [ -f "$FILE_IPV4" ] ; then
    rm "$FILE_IPV4"
fi

if [ -f /usr/bin/curl ]; then
        HTTP_STATUS=$(curl -sw '%{http_code}' -o $FILE_IPV4 $CLOUDFLARE_IPSV4)
        if [ "$HTTP_STATUS" -ne 200 ]; then
                echo "FAILED. Reason: unable to download IPv4 list [Status code: $HTTP_STATUS]"
                exit 1
        fi
else
        echo "FAILED. Reason: curl wasn't found on this system."
        exit 1
fi

sed -i ':a;N;$!ba;s/\n/ /g' $FILE_IPV4

sed -i '1s/^/trusted_proxies /' $FILE_IPV4

exit 0

Now, whenever I re-build my Caddy container, the “cloudflare-proxies” file is mounted with the container and the host updates that file every half hour.

Hopefully someone finds this helpful.

3 Likes

Please note that this alone is incomplete, because Cloudflare does not sanitize the X-Forwarded-For header by default, so even with this, you’re potentially vulnerable to remote IP spoofing. So you must configure Cloudflare to remove the X-Forwarded-For header, then write the header again with the source IP address, to make sure it doesn’t have untrusted values.

There’s instructions here in Authelia’s docs (not yet live on their site, so the source on Github will have to do for now):

5 Likes

Hi! Sorry to hijack this thread but is there any alternative to performing this import every time the reverse_proxy directive is used. It could also be done with a snippet and an arg but I wanted to ask if you know of any other way to have some sort of global trusted_proxies now that realip doesnt seem to work.

1 Like

There’s no global way.

Yes, you can use import with snippets.

3 Likes

GitHub - WeidiDeng/caddy-cloudflare-ip is needed

trusted_proxies cloudflare {
    interval 12h
    timeout 15s
}

being new to caddy the response didn’t quite have enough detail for me to get it working.

So for other newbies, I had to place it in the global section at the top of my Caddyfile.

I also had to to a custom build of caddy to include the trusted proxies cloudflare module. I’ve included my docker file as well.

{
	# Email for Let's Encrypt notifications
	email {$EMAIL}
	# ACME CA URL allows us to flip between production and staging.
	# we default to staging.
	acme_ca ${ACME_URL:https://acme-staging-v02.api.letsencrypt.org/directory}

	log {
		output file /var/log/caddy/all.log
	}

	# trigger the cloudflare ip module that periodically fetches
    # the list of valid cloud flare proxy IP addresses.
	servers {
		trusted_proxies cloudflare {
			interval 12h
			timeout 15s
		}
	}
}
# we build our own caddy file as we need the cloudflare module.
FROM caddy:2.9.1-builder AS builder

# https://caddyserver.com/docs/modules/dns.providers.cloudflare
RUN xcaddy build \
    --with github.com/caddy-dns/cloudflare \
    --with github.com/WeidiDeng/caddy-cloudflare-ip

FROM caddy:2.9.1

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

COPY config/caddy /etc/caddy

# CMD ["tail", "-f", "/dev/null"]

CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]