Best practice with one domain for both int. & ext. network

1. The problem I’m having:

Hi!

Today I’m using caddy to reverse proxy some services on my server to the public. domain.tld and *.domain.tld is configured on cloudflare to my public IP. Caddy using DNS challenging.

But now I want to reverse proxy all my local services on the local network, with the same public domain I already have.
So what is the best practice to do so? I want to reverse proxy everything but only a few services should get public access.

If i use for example app.domain.tld, is it enough, security wise, to use “not remote_ip” for it to not get exposed to the public?

3. Caddy version:

2.9.1

4. How I installed and ran Caddy:

Docker

a. System environment:

Ubuntu server 22.04.5. Run all services in Docker.

c. Service/unit/compose file:

version: "3.7"
services:
  caddy:
    container_name: caddy
    # image: caddy:latest
    build: .
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
      - 443:443/udp
    volumes:
      - ./data/Caddyfile:/etc/caddy/Caddyfile
      - ./data/data:/data
      - ./data/config:/config
    networks:
      - default
      - app1_default
      - app2_default
      - app3_default
      - app4_default
networks:
  app1_default:
    external: true
  app2_default:
    external: true
  app3_default:
    external: true
  app4_default:
    external: true

d. My complete Caddy config:

*.domain.tld {
    tls {
        dns cloudflare 123456789
    }

    @app1 host app1.domain.tld
    handle @app1 {
        reverse_proxy app1:123
    }

    @app2 host app2.domain.tld
    handle @app2 {
        reverse_proxy app2:123
    }

    @app3 host app3.domain.tld
    handle @app3 {
        reverse_proxy app3:123
    }

    @app4 host app4.domain.tld
    handle @app4 {
        reverse_proxy app4:123
    }

    # Fallback for otherwise unhandled domains
    handle {
        abort
    }
}

Your DNS likely can be configured to specify subdomains as local IPs. If you can configure your DNS settings with the domain provider, then that’s easily achievable.

What I did to access local services while not home is set up WireGuard VPN. There’s more to security than encryption, and your Docker network configuration has a lot to do with it.

If you want to be paranoid like I am, I switched from Docker to rootless Podman. It essentially runs the same as Docker due to all commands essentially being the same, but the Podman Compose translator doesn’t work as great from what I hear. I just use Quadlets, basically the same concept as compose.yaml but in a different format. It took a few hours to learn, but being integrated with systemd and even configuring Caddy to use socket-activated socket instead of directly exposing ports is much better.

If you want to stick with Docker, share the details of your networks and me or someone else can give guidance to tighten up security. If this is simply a home server like mine, we are unlikely to be targeted by any serious ne’er-do-wells who know what they’re doing.

Tl;dr, it’s always best security to have as many things only accessible on the local network. It’s limiting the opportunities for attack. Cloudflare DNS should be able to have subdomains as local IPs.

1 Like

May be that I have explained myself badly, but what I want to do is that I want to use my domain name for both internal and external, not just external.

What I have done to solve my problem is that I am now running with two instances of caddy, one for internal reverse proxy and the other for external reverse proxy.

This way I can reverse proxy all the services I have for my local network, then I reverse proxy the few services I want externally accessible on the other Caddy instance.

Just trying to clarify, so you want domain.tld, which is accessible publicly, to have subdomains using the wildcard *.domain.tld, and some of those services you want internal while others are external? If so, that’s pretty easy to do.

Yes, I want to reverse proxy my services to different subdomains. But not some services, I want to reverse proxy ALL services internal but only a few of them (the ones I choose) external.

Any domain that is set with a public (external) IP address will be available outside the network, assuming the configuration is correct. Any domain that has an IP within IANA-reserved ranges for private networks are only accessible in the internal network. Those ranges are
10.0.0.0/8: (10.0.0.0 to 10.255.255.255)
172.16.0.0/12: (172.16.0.0 to 172.31.255.255)
192.168.0.0/16: (192.168.0.0 to 192.168.255.255)

So for example, since I use Namecheap as my domain provider and they control the DNS by default, this is a sample of my URLs.

As you see, the host record (@) is pointed to a public IP. The rest that are listed are pointed to a single internal IP. Only the host record famdam.top is available outside in the scary world. I have others that are also publicly available, but they are not listed in this screenshot.
This wouldn’t change anything in my Caddyfile, because I have my reverse_proxy oriented at the service using Podman’s (Docker works, too) DNS resolution. Part of it is this:

*.famdam.top {
        tls {
                dns namecheap {
                        api_key REDACTED
                        user REDACTED
                        api_endpoint https://api.namecheap.com/xml.response
                        client_ip 139.60.65.236
                }
        }
        bind fd/3 {
                protocols h1
        }
        bind fd/4 {
                protocols h1 h2
        }
        bind fdgram/5 {
                protocols h3
        }
        @pihole host pihole.famdam.top
        handle @pihole {
                reverse_proxy pihole:80 {
                        header_up X-Forwarded-For {http.request.header.X-Real-IP}
                }
        }

So the named matcher @pihole is using host matcher to reference pihole.famdam.top. Then I’m using the handle directive with the name matcher @pihole to reverse_proxy pihole:80. pihole is a hostname in one of the networks Caddy is using, so it automatically points to the IP of the container.

So as long as you configure settings within your domain provider’s DNS panel, only the services with an internal IP can be accessible within Caddy’s configured network.

Do the internal IP A record prevent someone to add your subdomain and public IP in their host file to get access? Haven’t thought about that. Thanks!

Sorry I missed your reply.

Yeah, it doesn’t work like that. Private IP range is per network. So an IP range of 192.168.0.0/16: (192.168.0.0 to 192.168.255.255) is one of the options. No requests with that IP should be leaving the network and forwarded as a DNS request. Your 192.168.1.1 is different than my 192.168.1.1.

A certificate is still only issued when it verifies that YOU are the owner of the domain. That’s always through an API key tied to your profile with the provider. No risk there.

Note that putting RFC1918 private IP addresses in public DNS can be a form of attack called DNS Rebinding, and some local DNS resolvers will block those names to prevent it. See DNS rebinding - Wikipedia

1 Like