V2: IP whitelisting with Wireguard

1. Caddy version (caddy version):

caddy:2-alpine
Running it from a docker-compose file.

2. How I run Caddy:

docker-compose
Here ist my current docker-compose:

---
version: "3.7"

networks:
  proxy:
    external: true
  internal:
    external: false
    driver: bridge

services:
  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./config/Caddyfile:/etc/caddy/Caddyfile
      - /config/caddy/data:/data
      - /config/caddy/config:/config
    networks:
      - proxy
      - internal

  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    restart: unless-stopped
    command: --http-enabled
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data:/data
    ports:
      - "127.0.0.1:9000:9000"
    environment:
      - url=https://portainer.example.com


a. System environment:

Ubuntu 20.04 LTS running on a KVM VPS
Docker Version: 20.10.15
Docker-Compose Version: 2.5.0

b. Command:

docker-compose up

d. My complete Caddyfile or JSON config:

{
    email info@example.com
    # Optional staging lets encrypt for testing. Comment out for production.
    acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}

portainer.example.com {
    reverse_proxy portainer:9000
    @blocked not remote_ip MY_VPS_IP
    respond @blocked "Access Denied" 403
    header {
        # enable HSTS
        #Strict-Transport-Security max-age=31536000;
        # disable clients from sniffing the media type
        X-Content-Type-Options nosniff
        # clickjacking protection
        X-Frame-Options DENY
        # keep referrer data off of HTTP connections
        Referrer-Policy no-referrer-when-downgrade
        # Content-Security-Policy: default-src 'self'
    }
}

wireguard.example.com {
    reverse_proxy wg-easy:51821
    header {
        # enable HSTS
        #Strict-Transport-Security max-age=31536000;
        # disable clients from sniffing the media type
        X-Content-Type-Options nosniff
        # clickjacking protection
        X-Frame-Options DENY
        # keep referrer data off of HTTP connections
        Referrer-Policy no-referrer-when-downgrade
        # Content-Security-Policy: default-src 'self'
    }
}

Might be relevant, my docker-compose for Wireguard:

---
version: "3.8"
services:
  wg-easy:
    environment:
      # ⚠️ Required:
      # Change this to your host's public address
      - WG_HOST=wireguard.example.com

      # Optional:
      - PASSWORD=PASSWORD
      - WG_PORT=51820
      # - WG_DEFAULT_ADDRESS=10.8.0.x
      - WG_DEFAULT_DNS=1.1.1.1
      # - WG_MTU=1420
      # - WG_ALLOWED_IPS=x.x.x.x
      - url=https://wireguard.example.com

    image: weejewel/wg-easy
    container_name: wg-easy
    volumes:
      - .:/etc/wireguard
    ports:
      - "127.0.0.1:51820:51820/udp"
      - "127.0.0.1:51821:51821/tcp"
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1
    networks:
     - proxy


networks:
  proxy:
    external: true
  internal:
    external: false
    driver: bridge

3. The problem I’m having:

I’m trying to get whitelist working to have access to various apps running on the same Server while connected to Wireguard (Portainer, Grafana, Prometheus, etc. - In this example I’m only using Portainer as a starting point).

I’m using UFW with this workaround for Docker and allowed 127.0.0.1 on Port 9000.

Accessing Portainer without connected to Wireguard VPN works without a problem.
But as soon as I enable the whitelisting I can’t access it when connected to Wireguard. (Getting the defined message in the Caddyfile - “Access Denied”).

4. Error messages and/or full log output:

No errors when connecting to Portainer while connected to Wireguard.

5. What I already tried:

I’ve tried to whitelist the Server IP (which I got from here) aswell as the all IPs I got from executing ip a on the server and the sandbox IP I got from Wireguard.

Disabling UFW doesn’t work either.

The Docker network is present (docker network create proxy).

Checked a similar topic on your forums here: V2: basicauth + ip-based whitelisting - Help - Caddy Community

Read the docs which are linked in that topic.

I’m not sure if this Docker or Caddy related, since I learned about Docker like 3 weeks ago and just switched from Traefik to Caddy.
So it might be a networking issues on my end here.

Thank you very much for you help!

Docker might be running with a userland proxy, which transforms the TCP packets to make it look like the requests are coming from Docker’s network itself instead of from outside.

Turn on access logging in Caddy with the log directive to see in your logs what remote IP Caddy is seeing in the requests.

1 Like