2.5.0 and trusted_proxies

:80

reverse_proxy /api/* http://frontend_server:3000
reverse_proxy /sw.js http://frontend_server:3000
reverse_proxy /_* http://frontend_server:3000
reverse_proxy /about-us* http://frontend_server:3000
reverse_proxy /socket.io/* http://frontend_server:3000

reverse_proxy * http://monolith:80

This “neat” config has to be changed to, because of changes in 2.5.0

:80

reverse_proxy /api/* http://frontend_server:3000 {
    trusted_proxies private_ranges
}
reverse_proxy /sw.js http://frontend_server:3000 {
    trusted_proxies private_ranges
}
reverse_proxy /_* http://frontend_server:3000 {
    trusted_proxies private_ranges
}
reverse_proxy /about-us* http://frontend_server:3000 {
    trusted_proxies private_ranges
}
reverse_proxy /socket.io/* http://frontend_server:3000 {
    trusted_proxies private_ranges
}

reverse_proxy * http://monolith:80 {
    trusted_proxies private_ranges
}

Is there a global option, where I can specify trusted_proxies, or would it make sense to trust private_ranges by default in Caddy ?

Yours
Mads Jon Nielsen

You know, that’s actually a good idea now that you mention it… I don’t know many use cases where external front-facing proxies vary per-site. I suspect most users would want the option of specifying the list just once.

@francislavoie, what are your thoughts on this?

We generally try to avoid making exceptions like this in general (i.e. snippets are the correct way to reuse config, or have config apply to multiple sites), but this seems like a reasonable exception, maybe?

Not currently, no.

For your specific config, you have very redundant usage of reverse_proxy. Try not to repeat reverse_proxy so many times with the same upstream.

I’d recommend writing your config like this instead:

:80 {
	@frontend path /api/* /sw.js /_* /about-us* /socket.io/*
	handle @frontend {
		reverse_proxy frontend_server:3000 {
			trusted_proxies private_ranges
		}
	}

	handle {
		reverse_proxy monolith:80 {
			trusted_proxies private_ranges
		}
	}
}

The handle aren’t necessary in this situation, but they make the config more flexible to future changes, such as if you wanted to use respond or file_server according to certain matchers, because otherwise you’d have to contend with directive ordering to make sure it works right.

No, that wouldn’t be safe.

At a glance it sounds like it should be, but there are situations where it’s risky. So it’s best to default to trusting nothing, and letting the user opt-in, should they understand the implications.

For example, if you’re running in Docker and don’t have a downstream proxy in front of it, Docker may be running with a userland proxy (TCP layer) which makes the RemoteAddr that Caddy sees actually be a private IP for all requests. In that situation, there’s nothing that can reasonably be trusted, because the userland proxy is lossy (rewrites/hides/loses the original client IP on the TCP connection). Obviously your application would get garbage IPs in that situation (172.x.x.x range probably) but that’s still better than allowing any client to spoof X-Forwarded-For.

1 Like

Right, thanks for the “dry” tips, they will be put in production :smile:
I’m closing this issue.

2 Likes