Request for comment: path based reverse proxy with redirect and remote_ip check

1. The problem I’m having:

I have to reverse proxy three services on three different paths on the same host. The services should only be reachable from a certain IP address, which is an internal NAT gateway. Also a path without trailing / should be redirected to the path with trailing /. The services themselves are served upstream from the same path as given in the Caddy config, so handle works here. Here is what I have so far:

service.example.com {
	@internal-gw remote_ip 10.11.12.13/32

	redir /service1 /service1/ 308
	handle /service1/* {
		reverse_proxy @internal-gw http://service1.example.com
	}

	redir /service2 /service2/ 308
	handle /service2/* {
		reverse_proxy @internal-gw http://service2.example.com
	}

	redir /service3 /service3/ 308
	handle /service3/* {
		reverse_proxy @internal-gw http://service3.example.com
	}
}

This works as expected, with just one little exception: the redir directives don’t check for the @internal-gw, possibly leaking that there is some kind of service due to the redirect, that would be sent regardless of the remote_ip. Accessing the services from other clients doesn’t work due to the reverse_proxy directive with the @internal-gw matcher.

What would be the recommended way to configure this, so that the service names don’t leak? Maybe with a route like given below?

service.example.com {
	@internal-gw remote_ip 10.11.12.13/32

	route @internal-gw {
		redir /service1 /service1/ 308
		handle /service1/* {
			reverse_proxy http://service1.example.com
		}

		redir /service2 /service2/ 308
		handle /service2/* {
			reverse_proxy http://service2.example.com
		}

		redir /service3 /service3/ 308
		handle /service3/* {
			reverse_proxy http://service3.example.com
		}
	}
}

Or would a different approach be better? I also have to consider, that at some point one of the services should be reachable from any address. With the route approach I would move the redir and handle directive for this service out of the route directive.

One other approach I was thinking of was using a named matcher for the redir directives like below. But this would mean creating a named matcher for each service.

@service3 {
	remote_ip 10.11.12.13/32
	path /service3
}

redir @service3 /service3/ 308
handle /service3/* {
	reverse_proxy @internal-gw http://service3.example.com
}

Any recommendations and comments are welcome! :slight_smile:

2. Error messages and/or full log output:

n/a

3. Caddy version:

v2.9.1

4. How I installed and ran Caddy:

a. System environment:

Docker in a Nomad cluster

b. Command:

n/a

c. Service/unit/compose file:

n/a

d. My complete Caddy config:

n/a

5. Links to relevant resources:

n/a

1 Like
Yes, the way you specified the `route` directive should work. A redirect will not happen without the required `remote_ip` range or addresses.
service.example.com {
	@internal-gw remote_ip 10.11.12.13/32

	route @internal-gw {
		redir /service1 /service1/ 308
		handle /service1/* {
			reverse_proxy http://service1.example.com
		}

		redir /service2 /service2/ 308
		handle /service2/* {
			reverse_proxy http://service2.example.com
		}

		redir /service3 /service3/ 308
		handle /service3/* {
			reverse_proxy http://service3.example.com
		}
	}
}

I don’t believe using a named matcher with redir wil work at all.

Thank you. I implemented the route variant and it works es expected.