Limiting specific subdomains to internal IP ranges

1. The problem I’m having:

I’m using Caddy as a proxy for several subdomains via a wild card domain cert. I’m having trouble configuring my Caddyfile to achieve the following:

  • Specific subdomains should be limited to just internal IP addresses/ranges
  • All subdomains should be limited to Cloudflare IP addresses using the Cloudflare IP module (GitHub - WeidiDeng/caddy-cloudflare-ip)

In the example below, I’d like to limit the ‘books’ subdomain to internal IP ranges, while allowing anyone with either a Cloudflare-forwarded IP or internal IP to access the ‘nextcloud’ subdomain.

I’ve found several examples online, but none of them also incorporate the syntax for wild card entries, which is why I’m struggling to implement it from scratch myself. I’m also trying to figure out to implement the whitelisting globally so I don’t have to manually specify the networks for each subdomain’s entry (for example, this suggestion requires specifying the allowed IP addresses for every subdomain: linux - Caddy V2 IP whitelist - Stack Overflow).

2. Error messages and/or full log output:

N/A

3. Caddy version:

Caddy Docker v2.7.6

4. How I installed and ran Caddy:

a. System environment:

Debian 10, Docker (caddy:2)

b. Command:

N/A

c. Service/unit/compose file:

  caddy:
    build: /mnt/user/appdata/caddy/.
    container_name: caddy
    restart: unless-stopped
    environment:
      CF_API_TOKEN: ${CF_API_TOKEN}
    volumes:
      - ${APPDATA}/caddy/Caddyfile:/etc/caddy/Caddyfile
      - ${APPDATA}/caddy/site:/srv
      - ${APPDATA}/caddy/data:/data
      - ${APPDATA}/caddy/config:/config
    networks:
      - caddy

d. My complete Caddy config:

{
	email <my e-mail>
	
	servers {
	
	  trusted_proxies cloudflare {
        interval 12h
        timeout 15s
      }
	  
	}
}

*.domain.xyz, domain.xyz {

	tls {
		dns cloudflare {env.CF_API_TOKEN}
	}
	
	@books host books.domain.xyz
	handle @books {
	  reverse_proxy calibre-web:8083
	}

	@nextcloud host nextcloud.domain.xyz
	handle @nextcloud {
	  reverse_proxy nextcloud:443
	}
	
}

5. Links to relevant resources:

1 Like

You can probably do it like this:

A named matcher in the wildcard domain itself, and then referencing it with an abort in each subdomain when needed. The abort statement will close all connections from IPs that are not the specified client_ips.

*.domain.xyz, domain.xyz {
        @domain-xyz-client-ip {
                not client_ip 192.168.0.0/16 10.1.1.1/32
        }

        tls {
                dns cloudflare {env.CF_API_TOKEN}
        }

        @books host books.domain.xyz
        abort @domain-xyz-client-ip
        handle @books {
                reverse_proxy calibre-web:8083
        }

        @nextcloud host nextcloud.domain.xyz
        abort @domain-xyz-client-ip
        handle @nextcloud {
                reverse_proxy nextcloud:443
        }
}

There’s also the GitHub - tuzzmaniandevil/caddy-dynamic-clientip: http.matchers.dynamic_client_ip matches requests by the client IP address, the ip addresses to match against is provided my a module that implements IPRangeSource plugin which provides a matcher that can take IP source modules (e.g. Weidi’s Cloudflare plugin).

@Monviech that’s not quite right, because you used the same abort @domain-xyz-client-ip line multiple times in the same context (duplicate config, ineffectual).

But yes, you may use the abort directive to close the connection immediately, or the error directive if you want to trigger an error that you can handle with handle_errors and serve a custom/friendly error page.

@francislavoie

Hmm yeah you are right, it should probably be inside each subdomain handle, right?

E.g.

        @books host books.domain.xyz
        handle @books {
                abort @domain-xyz-client-ip
                reverse_proxy calibre-web:8083
        }

@Monviech @francislavoie

Can I declare multiple lines in an IP range name? For instance, if I want to block anything that’s not an internal IP range OR Cloudflare, could I use something like this?

        @blocked-ranges {
                not client_ip 192.168.0.0/16 10.1.1.1/32
                not client_ip <cloudflare ip ranges>
        }

And then abort @blocked-ranges in the appropriate subdomain routes?

Essentially, yeah.

I suggest using the dynamic_client_ip plugin for that, but yeah.

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