V1: Block or allow certain paths to a backend, using Caddy proxy

1. My Caddy version (caddy version):

Version 1

2. My Caddyfile

server.company.com:443 {
	log ./logs/access.log
	#tls self_signed

	proxy / http://localhost:8888 {
		transparent
		websocket
		insecure_skip_verify 
	}
		
	proxy /login 127.0.0.1:4200 {
		transparent
	}
}

3. The problem I’m having:

Okay, I’m trying to get my head around how the Caddyfile works.

I’m trying to setup a reverse proxy, that either blocks certain paths, or preferably whitelists allowed paths.

So that the Caddy proxy can behave differently for:

5. What I already tried:

Tried reading and searching the docs and forum. But have not yet found the entrypoint for what I want to achieve.

Any hints, please?

The list of either allowed path or blocked paths is a bit longer than 2, maybe 3-10, depending on a few things.

Long enough that I’d rather not repeat it more than once. : )

Hi @vbakke,

In Caddy v1 you’ll need to rely on rewrite hacks in order to achieve this. Whitelist is slightly more complicated than blacklist, but they’re both doable. See examples with comments:

# Proxy from whitelist
example.com {
  # Prepend with special prefix to proxy from
  rewrite {
    if {path} starts_with /foo
    if {path} starts_with /allowed
    if {path} starts_with /path
    to /proxywhitelist{uri}
  }

  # Stop malicious actors from abusing your proxy prefix
  # to get non-whitelisted requests past the whitelist
  # - this works because only one rewrite goes off, either
  # the proxy prefix or this security measure. Nobody should
  # ever be requesting your proxy prefix directly.
  rewrite /proxywhitelist {
    to /forbidden
  }
  status 403 /forbidden

  # Proxy and strip prefix to make this process transparent
  proxy /proxywhitelist 127.0.0.1 {
    without /proxywhitelist
  }
}
# Proxy except for blacklist
example.com {
  # We use status here to blacklist paths directly with 403
  # responses, but you can also use a bulk rewrite to handle
  # the blacklist with another directive if you need to,
  # just adapt the other example into a blacklist
  status 403 {
    /bar
    /blacklisted
    /denied
  }

  # Proxy everything else
  proxy / 127.0.0.1
}

In v2, this is ludicrously easier with matchers. It’s hard to state just how much better this kind of scenario is is handled in v2.

2 Likes

@Whitestrake For bonus points :taco:, what would this config (approximately) look like in v2?

1 Like

v2 configuration:

# Proxy from whitelist
example.com {
  @whitelisted {
    path /foo* /allowed* /path*
  }

  reverse_proxy @whitelisted 127.0.0.1
}
# Proxy non blacklisted
example.com {
  @notblacklisted {
    not {
      path /bar* /blacklisted* /denied*
    }
  }

  reverse_proxy @notblacklisted 127.0.0.1
}

Alternately to proxying the non-blacklisted paths and leaving blacklisted responses ambiguous (probably 404s), you could remove the not and instead handle it as a blacklist by responding with a 403 (or other response as desired), then reverse proxying everything else.

3 Likes

Nice! Thank you! There’s no way I would have figured out the V1 all by myself.

V2 will be a definite choice as soon as the beta label is gone. :slight_smile:
(Yes, I know it’s production ready, but I just can’t bring myself to installing security software on clients’ servers with a ‘beta’ suffix.)

Greatly appreciated that you provided solutions for both versions.


Just a side question about how the wildcard is matching urls.

I have a “folder” prefix in the URL which might be blank, or a folder.

If I wanted to match:

  • /bar
  • /prefix1/bar
  • /prefix2/bar

Would /*/bar suffice? Or would that fail on /bar?
Any differences between V1 and V2 on wildcards?

In v1 there is no wildcard matching (*), per se, although there is regex matching (in v1 and v2).

You can rewrite if {path} starts_with or not_starts_with or contains etc (a full list of conditionals is here: https://caddyserver.com/v1/docs/rewrite#if-conditions).

Even with v2, I’d also be wary of /*/bar. It’s not limited by element - it’s a globular match, so it would hit /prefix1/bar, /prefix2/bar, but also /some/really/long/path/bar.

(Note also that as a globular match, /foo* will be satisfied by /foo/bar but also /foobar - take note of that if matching a specific path element is important! /foo/* might be better in some cases.)

You can use a path_regexp matcher in v2 for those ones in order to be safe when dealing with strict path elements.

2 Likes

Okay. Thank you, once again.

In my case, ‘bar’ will be unique and appear only once in any legal url.

So status 404 {path} contains /bar/ might work?
But then, status doesn’t have conditions, do they?

Nope, status itself doesn’t support those conditionals. Only rewrite and redir do.

You’ll need to rewrite to some path that you then use status on.

rewrite {
  if {path} contains /bar/
  to /notfound
}
status 404 /notfound

(in v2 this is a one-liner, respond */bar/* 404)

2 Likes

Thank you so much! I should have enough to tinker a bit, now.

Examples with context and explanations are just so mush more helpful than just examples.

Appreciate your help! :sunglasses:
Vegard

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.