Handle catch-all subdomain and IP filter at the same time?

1. Output of caddy version:

v2.6.2 h1:wKoFIxpmOJLGl3QXoo6PNbYvGW4xLEgo32GPBEjWL8o=

2. How I run Caddy:

a. System environment:

Ubuntu 22.04.1 LTS x86_64 GNU/Linux

b. Command:

caddy run --config ./Caddyfile

c. Service/unit/compose file:

N/A

d. My complete Caddy config:

{
        auto_https off
        local_certs
        skip_install_trust
}

*.example.com:80, example.com:80 {

        # Serve Public Pages

        @root host example.com
        handle @root {
                respond "Home Page" 200
        }

        @public host public.example.com
        handle @public {
                respond "Public Page" 200
        }

        # Check if IP in allow list for private pages

        @myipfilter {
                not remote_ip 192.168.0.0/16
                not remote_ip 172.16.0.0/12
                not remote_ip 10.0.0.0/8
        }
        handle @myipfilter {
                respond "Access denied" 403
        }

        # Serve Private Pages

        @private1 host private1.example.com
        handle @private1 {
                respond "Private Page 1" 200
        }

        @private2 host private2.example.com
        handle @private2 {
                respond "Private Page 2" 200
        }

        @private3 host private3.example.com
        handle @private3 {
                respond "Private Page 3" 200
        }

        # Fallback for otherwise unhandled domains

        handle {
                redir http://example.com
        }
}

3. The problem I’m having:

I am trying to achieve the following logic in caddy:

  1. If requesting certain public hosts, serve:
  2. If requesting certain private hosts, check if IP in allow list, then serve or reject;
  3. If requesting unmatched wildcard hosts (catch-all), redirect to certain public host in 1.

Basically like this decision tree:

Which means, a desired response should be:

$ curl http://catch-all.example.com -vL \
          --resolve catch-all.example.com:80:127.0.0.1 \
          --resolve example.com:80:127.0.0.1

* Added catch-all.example.com:80:127.0.0.1 to DNS cache
* Added example.com:80:127.0.0.1 to DNS cache
* Hostname catch-all.example.com was found in DNS cache
*   Trying 127.0.0.1:80...
* Connected to catch-all.example.com (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: catch-all.example.com
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Found
< Location: http://example.com
< Server: Caddy
< Date: Sat, 10 Dec 2022 08:53:01 GMT
< Content-Length: 0
<
* Connection #0 to host catch-all.example.com left intact
* Issue another request to this URL: 'http://example.com/'
* Hostname example.com was found in DNS cache
*   Trying 127.0.0.1:80...
* Connected to example.com (127.0.0.1) port 80 (#1)
> GET / HTTP/1.1
> Host: example.com
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Server: Caddy
< Date: Sat, 10 Dec 2022 08:53:01 GMT
< Content-Length: 9
<
* Connection #1 to host example.com left intact

Home Page⏎

4. Error messages and/or full log output:

Currently undesired response from the my Caddyfile:

$ curl http://catch-all.example.com -vL \
          --resolve catch-all.example.com:80:127.0.0.1 \
          --resolve example.com:80:127.0.0.1

* Added catch-all.example.com:80:127.0.0.1 to DNS cache
* Added example.com:80:127.0.0.1 to DNS cache
* Hostname catch-all.example.com was found in DNS cache
*   Trying 127.0.0.1:80...
* Connected to catch-all.example.com (127.0.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: catch-all.example.com
> User-Agent: curl/7.81.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 403 Forbidden
< Content-Type: text/plain; charset=utf-8
< Server: Caddy
< Date: Sat, 10 Dec 2022 08:55:04 GMT
< Content-Length: 13
<
* Connection #0 to host catch-all.example.com left intact

Access denied⏎

5. What I already tried:

N/A

6. Links to relevant resources:

N/A

Please fill out the help topic template, as per the forum rules.

You haven’t told us what’s not working. Show some symptoms. What’s in your logs? What behaviour are you seeing? Show some requests with curl -v.

Thank you, I have updated the top post according to the help topic template.

I think your remote_ip matching might be incorrect. Try this instead:

        @myipfilter not remote_ip private_ranges
        handle @myipfilter {
                respond "Access denied" 403
        }

Thank you, however the IP range here is for demonstration purpose only. Besides, according to Request matchers (Caddyfile) — Caddy Documentation :

A named matcher definition constitutes a matcher set. Matchers in a set are AND’ed together; i.e. all must match. For example, if you have both a header and path matcher in the set, both must match.

The myipfilter I used is correct, and is not related to the question I have in the top post.

The question I have here, instead, is regarding to how to deal with the fallback/catch-all handle at the last part of my Caddyfile. As I demonstrated in my decision tree, public ip should be able to access both the public host and catch-all host(*.example.com, so that they can be redirect to the home page), and only private ip can access private host. When public ip attempting to access a private host, They should received a 403 Forbidden error.

However, since the fallback/catch-all handle must be put in the last part of my Caddyfile, and handle @myipfilter must be put before any handle for private hosts, it conflicts with the fallback/catch-all handle. As a result, when a public ip attempting to access the catch-all host, it have to pass myipfilter first, which is undesired according to my decision tree.

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