How to deal with unhandled requests when using reverse_proxy directive

1. The problem I’m having:

I’m trying to regex match a request that is sent to the reverse proxy directive but I can’t seem to find out how to handle non matching requests. I tried using a handle doing something like “not @whitelisted” but that seems to get ignored.

3. Caddy version: 2.6.4

4. How I installed and ran Caddy:

Archlinux package running with systemd unit file

a. System environment:

Archlinux either with systemd unit file or just from the command line

b. Command:

caddy run ./Caddyfile 

d. My complete Caddy config:

disco.pandaden.net {
	bind "39.52.43.18"

	@whitelisted { 
	      path_regexp \?device=([A-Za-z0-9]+(-[A-Za-z0-9]+)+)
	}
       
    	reverse_proxy @whitelisted 10.0.15.1:8443 {

      	 header_up X-Forwarded-For {http.request.remote.host}
      	 header_up X-Client-Port {http.request.remote.port}
      	 header_up X-Tls-Client-Cert-Der-Base64 {http.request.tls.client.certificate_der_base64}
    	}

    	tls {
      		client_auth {
        		mode request
      	}
    }
}

I recommend using handle blocks.

Apply your matcher to the first handle block, and put your proxy inside it.

Make a second handle block with no matcher, and all other requests will fall through to it.

Remove this. Caddy sets this header correctly already. See reverse_proxy (Caddyfile directive) — Caddy Documentation

This is unlikely to be useful because the connecting port is usually a random port assigned by the system networking stack.

You can shorten this by using the placeholder shortcut {tls_client_certificate_der_base64}

Thank you for your help. I modified the file and it looks like it’s working now.

The only problem I have now is with the path_regex

path_regexp /\?device=([A-Za-z0-9]+(-[A-Za-z0-9]+)+)

The regex seems to be correct according to this validator regex101: build, test, and debug regex
but it seems like in caddy this is not evaluated.

Here is my updated Caddyfile

disco.pandaden.net { 
        bind "39.52.43.18" 
 
        @whitelisted {  
              path_regexp  /\?device=([A-Za-z0-9]+(-[A-Za-z0-9]+)+) 
 
 
        } 
         
        handle @whitelisted {    
          reverse_proxy 10.0.15.1:8443 { 
 
            header_up X-Tls-Client-Cert-Der-Base64 {tls_client_certificate_der_base64} 
            } 
        } 
        tls { 
                client_auth { 
                        mode request 
        } 
    } 
 
    handle {  
        respond "no match"       
    } 
 
}
~              

The tls directive cannot be within a handle block.

Your config is really messy, please mind whitespace. Run caddy fmt --overwrite to clean it up.

Reformatted

disco.pandaden.net {
	bind "39.52.43.18"

	@whitelisted {
		path_regexp /\?device=([A-Za-z0-9]+(-[A-Za-z0-9]+)+)
	}

	handle @whitelisted {
		reverse_proxy 10.0.15.1:8443 {
			header_up X-Tls-Client-Cert-Der-Base64 {tls_client_certificate_der_base64}
		}
	}
	tls {
		client_auth {
			mode request
		}
	}

	handle {
		respond "no match"
	}

Oh right – path_regexp cannot match the query, because the query is not the path. See What is a URL? - Learn web development | MDN

I think you might need to use an expression matcher in this case to regexp match on the {uri} placeholder

@whitelisted `{uri}.match('/\?device=([A-Za-z0-9]+(-[A-Za-z0-9]+)+)')`

That said, this is awkward because it won’t match if device is somewhere else in the query, i.e. as a second query param.

Thank you, I thought path would match everything right of the url, but it makes sense that it would only match paths.

It’s ok for device as this shouldn’t be anywhere else, since this is defined in the syncthing relay protocol.

I’m just a bit lost on how to integrate the {uri].match into the caddyfile - as far as I can see it’s used to replace something ?

It’s an expression matcher. See Request matchers (Caddyfile) — Caddy Documentation. You can remove your existing @whitelisted matcher (i.e. path_regexp) and use what I gave you instead.

Thank you for the hint.

But I’m still a bit lost on how to integrate the expression matcher.
As I understood it it should go like this :

disco.pandaden.net {
	bind "39.52.43.18"

        @whitelisted `{uri}.match('/\?device=([A-Za-z0-9]+(-[A-Za-z0-9]+)+)')`
	
	handle @whitelisted {
		reverse_proxy 10.0.15.1:8443 {
			header_up X-Tls-Client-Cert-Der-Base64 {tls_client_certificate_der_base64}
		}
	}
	tls {
		client_auth {
			mode request
		}
	}

	handle {
		respond "no match"
	}

But that gives an error :

Error: loading initial config: loading new config: loading http app module: provision http: server srv0: setting up route handlers: route 0: loading handler modules: position 0: loading module 'subroute': provision http.handlers.subroute: setting up subroutes: route 0: loading matcher modules: module name 'expression': provision http.matchers.expression: compiling CEL program: ERROR: <input>:1:52: undeclared reference to 'match' (in container '')
 | caddyPlaceholder(request, "http.request.uri").match('/\?device=([A-Za-z0-9]+(-[A-Za-z0-9]+)+)')
 | ...................................................^

Ah sorry, it’s matches instead of match in CEL.

@whitelisted `{uri}.matches('/\?device=([A-Za-z0-9]+(-[A-Za-z0-9]+)+)')`

See https://github.com/google/cel-spec/blob/master/doc/langdef.md

2 Likes

Thank you, its working as expected now :).

1 Like