Security of public and private domains on the same machine

1. Caddy version (caddy version):

v2.4.5 h1:P1mRs6V2cMcagSPn+NWpD+OEYUYLIf6ecOa48cFGeUg=

2. How I run Caddy:

a. System environment:

via docker-compose

$ cat /proc/device-tree/model
Raspberry Pi 4 Model B Rev 1.2
$ cat /etc/debian_version
10.10
$ docker --version
Docker version 20.10.9, build c2ea9bc

b. Command:

sudo docker-compose stack up -d

c. Service/unit/compose file:

version: '3.9'

services:
  caddy:
    image: caddy:latest
    ports:
      - target: 80
        published: 80
        protocol: tcp
        mode: host
      - target: 443
        published: 443
        protocol: tcp
        mode: host
    volumes:
      - ./caddy/data:/data
      - ./caddy/config:/config
      - ./caddy/Caddyfile:/etc/caddy/Caddyfile
  hello_public:
    image: nginxdemos/hello
  hello_private:
    image: nginxdemos/hello

d. My complete Caddyfile or JSON config:

example.com:80 {
	reverse_proxy hello_public:80
}


localhost:80 {
	reverse_proxy hello_private:80
}

3. The problem I’m having:

I’d like to use caddy to serve both private and public services. In the example above example.com:443 would handle requests from the public internet and localhost:80 handle those on the machine itself, and eventually the LAN.

However, this is not secure in that proposed setup because someone could send a request to the IP resolved by example.com with the Host: localhost and get a response from what one might think is a private service.

I wanted to see what the options are to deal with this kind of problem. There may be something I’m not aware of to allow a single caddy to securely serve both public and private services from a single caddy instance/machine.

I’d ideally want to keep the set up as simple as possible, as I was hoping this single caddy server might have given me. Though it looks like I might need to have two separate physical machines host separate caddy servers, or figure out how to get the raspberry pi I’m using to split its network in two so it can have two IPs on the network with a different caddy instance bound to each.

Alternatively is it secure to trust the source IP of the packet making the request and only serve those on my subnet? or is that as easily spoofed as the Host?

Thanks for any ideas/confirmation I’m going down the right route.

4. Error messages and/or full log output:

N/a

5. What I already tried:

N/a

6. Links to relevant resources:

N/a

bind 127.0.0.1 is your friend (for the localhost site). :slight_smile:

You can use caddy adapt to look at the JSON and confirm that the sites are each bound to the correct network interface.

Ok, interesting. I’m not much of a networking person, so will have to play around to see how that bind works. Thanks for the steer.

Also from a hover over your avatar seems like you’re the author of the project. It’s a very nicely put together project, kudos.

1 Like

Thank you for the compliment! It’s all thanks to a helpful group of maintainers.

This won’t really work in Docker though, because connections from outside the container come on the IP address of the Docker network.

If you were to be running another app inside of the same Docker container, then this would work, but that’s not really the case.

Instead, what you want to do is bind on 127.0.0.1 in your Docker config instead:

But this would be for all sites, not just a few.

The best remaining option IMO is to use the remote_ip matcher on those sites to reject connections from clients that are not from one of the private IP ranges

@notPrivate not remote_ip 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
abort @notPrivate
1 Like

Ah yeah, I came back to this and realised the same thing. I also want to eventually be able to access via host name in the internal network: be that via a dns or just the <host>.local bonjour thingy (I think it’s called).

The rule to reject IPs not on the subnet is a good idea. I’ll do some reading to see if those IPs could also be spoofed from a external request.

Thanks

Best to use *.localhost (if you’re only ever connecting from the same machine) or *.home.arpa (for LAN). Mac devices do some truly wacky things with *.local domains, because they’re using that TLD for multi-cast DNS.

They can’t – it’s the IP address on the TCP packets themselves. Requests from outside your LAN would need to be targeting your WAN IP address (unless they got into your network with a VPN or something).

Best to use *.localhost (if you’re only ever connecting from the same machine) or *.home.arpa (for LAN). Mac devices do some truly wacky things with *.local domains, because they’re using that TLD for multi-cast DNS.

Ok, good to know, I’ll look t this *.home.arpa thing.

They can’t – it’s the IP address on the TCP packets themselves. Requests from outside your LAN would need to be targeting your WAN IP address (unless they got into your network with a VPN or something).

Ah, I think I see. I thought it was looking at the requests source IP, but actually we are saying check the destination, because local machines would point directly to the machine hosting caddy and external requests would point at the routers public IP.

Very much appreciate the help. and the links.

Thanks for your time.

They do – but the remote IP is higher level than HTTP, it’s at the TCP layer. Sorry if that was unclear – but point is, if the source IP on the packet was a LAN IP then your router would probably reject it, because it doesn’t make sense.

ah ok, appreciate the clarification.

Then my current (and shaky) understanding is that that IP could be spoofed, but I’d need to confirm that my router would reject it. There’s a section on ‘defence against spoofing attacks’ that sounds like what we are talking about here: https://en.wikipedia.org/wiki/IP_address_spoofing.

If anyone stumbles on this in the future, I ended up making use of the docker network type macvlan which will launch a container on, what appears on the network, as a physical device (own Mac address and host).

I then run two caddy services, one for internal and one for external services. Both able to bind and expose port 80 & 443 on their virtual hosts to the network.

Then on my router I’m port forwarding to the public caddy host ip:80/443.

I’ve then been able to configure nice internal and external domain resolves + https.

Currently thinking this satisfies my security concerns. happy to give more details if anyone stumbles on this and wants it.

1 Like