Remote_ip restriction on by default?

1. Caddy version (caddy version):

v2.2.1 (via caddy:2-alpine)

2. How I run Caddy:

docker + docker-compose

a. System environment:

Ubuntu 20.04.1 LTS
Docker version 19.03.13
docker-compose version 1.24.1

c. Service/unit/compose file:

version: "3.7"

services:
  caddy:
    image: lucaslorentz/caddy-docker-proxy:ci-alpine
    ports: ...
    volumes: ...
    labels:
      caddy.email: xxx
      caddy.acme_ca: xxx
      # snippet definitions
      caddy_0: '(www-redirect)'
      caddy_0.redir: 'https://www.{hostport}{uri}'
      caddy_1: '(dns-challenge)'
      caddy_1.tls.dns: 'lego_deprecated easydns'
      caddy_2: example.com
      caddy_2.import: 'www-redirect'

  whoami-private-working:
    image: jwilder/whoami
    networks:
      - docker_web
    labels:
      caddy: 'private-working.example.com'
      caddy.reverse_proxy: "{{upstreams 8000}}"
      caddy.@public_networks.not.remote_ip: '192.168.0.0/16 172.16.0.0/12 10.0.0.0/8'
      caddy.import: 'dns-challenge'
      caddy.respond: '@public_networks 403'
      caddy.respond.close:

d. My complete Caddyfile or JSON config:

Caddyfile generated by caddy-docker-proxy:

{
     acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
     email xxxxxxxx
}
(dns-challenge) {
     tls {
             dns lego_deprecated easydns
     }
}
(www-redirect) {
     redir https://www.{hostport}{uri}
}
example.com {
     import www-redirect
}
private-working.example.com {
     @public_networks {
             not {
                     remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8
             }
     }
     import dns-challenge
     respond @public_networks 403 {
             close
     }
     reverse_proxy 172.25.0.8:8000
}

3. The problem I’m having:

The above works. I am ONLY able to hit private-working.example.com if I’m on my private network. Here’s the rub: I’m trying to factor this out into something that makes it easy to declare a Docker container as private without a bunch of configuration.

5. What I already tried:

I tried defining snippet like this and it doesn’t work (public networks can access the site):

...
(internal-only) {
     @public_networks {
             not {
                     remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8
             }
     }
     respond @public_networks 403 {
             close
     }
}
private-not-working.example.com {
     import dns-challenge internal-only
     reverse_proxy 172.25.0.9:8000
}

What I would REALLY like to do is declare my Caddyfile so that all sites are private-network-only by default, but with a piece of configuration I can make a specific site be publicly exposed. It would look something like this:

{
    acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
    email xxxxxxxx
}
(make-me-externally-available) {
    # what do I do here to make this work?
    # ...
}
private.example.com {
    reverse_proxy 172.25.0.9:8000
}
public.example.com {
    import make-me-externally-available
	reverse_proxy 172.25.0.8:8000	
}

This line doesn’t quite make sense, this would be passing the argument internal-only to the dns-challenge snippet. You need two separate import lines.

Unfortunately I don’t think that’s possible. There has to be something telling Caddy to reject requests to private.example.com.

One option I suppose is you could use bind to listen on a specific network interface that’s only accessible in your LAN, instead of the default of 0.0.0.0 (everything).

FYI, you can shorten this:

     @public_networks {
             not {
                     remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8
             }
     }

to this:

     @public_networks not remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8

I’m not sure if this ends up any shorter with CDP but it might?

1 Like

@francislavoie thank you for taking the time to help me out. With your notes I have it working perfectly (and I’ll just live with not being able to invert things to being private by default).

Here’s my new docker-compose.yaml:

version: "3.7"

services:
  caddy:
    build: ./custom-caddy-build
    restart: ...
    environment: ...
    env_file: ...
    ports: ...
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      ...
    labels:
      # Global options
      caddy.email: ...
      caddy.acme_ca: https://acme-staging-v02.api.letsencrypt.org/directory
      # snippet definitions
      caddy_0: '(www-redirect)'
      caddy_0.redir: 'https://www.{hostport}{uri}'
      caddy_1: '(dns-challenge)'
      caddy_1.tls.dns: 'lego_deprecated easydns'
      caddy_2: '(internal-only)'
      caddy_2.@public_networks: 'not remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8'
      caddy_2.respond: '@public_networks 403'
      caddy_2.respond.close:
      # global directives
      caddy_4: example.com
      caddy_4.import: 'www-redirect'

  whoami-public:
    image: jwilder/whoami
    labels:
      caddy: 'public.example.com'
      caddy.reverse_proxy: "{{upstreams 8000}}"

  whoami-private:
    image: jwilder/whoami
    labels:
      caddy: 'private.example.com'
      caddy.reverse_proxy: "{{upstreams 8000}}"
      caddy.import_0: 'dns-challenge'
      caddy.import_1: 'internal-only'

Which produces the following Caddyfile:

{
    acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
    email ...
}
(dns-challenge) {
    tls {
            dns lego_deprecated easydns
    }
}
(internal-only) {
    @public_networks not remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8
    respond @public_networks 403 {
            close
    }
}
(www-redirect) {
    redir https://www.{hostport}{uri}
}
example.com {
    import www-redirect
}
private.example.com {
    import dns-challenge
    import internal-only
    reverse_proxy 172.25.0.4:8000
}
public.example.com {
    reverse_proxy 172.25.0.9:8000
}
1 Like

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