PascalMinder / geoblock

Traefik middleware plugin - Deny requests based on country of origin
108 stars 9 forks source link

Can't get the real client IP to make use of GeoBlock #34

Closed kowalcj0 closed 1 year ago

kowalcj0 commented 1 year ago

Hi,

I tried quite a few different setups but for some reason traefik can't get the real IP address of the client. As a result GeoBlock is not able to block any incoming requests because all requests are treated as local traffic. Here's an excerpt from traefik's log:

INFO: GeoBlock: 2023/04/29 18:23:32 Local ip allowed:  172.18.0.1

I'd be really thankful for indicating how to make treafik to get the real client IP so GeoBlock can do its job :)

Discovering Client IP

To check the client IP address on the host (rpi) I ran tcpdump with simple filter and the client IP was correct, e.g: 76.121.115.131:

portainer@portainer:~ $ sudo tcpdump -i eth0 -n 'port 80 or port 443'
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
18:24:57.534844 IP 76.121.115.131.25587 > 192.168.2.100.443: Flags [S], ...
18:24:57.534966 IP 192.168.2.100.443 > 76.121.115.131.25587: Flags [S.], ...
...

When I ran the same tcpdump filter inside the traefik container, then the IP address of the client was set to the proxy network gateway 172.18.0.1:

/ # apk add tcpdump
/ # tcpdump -i eth0 -n 'port 80 or port 443'
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
18:23:32.228121 IP 172.18.0.1.39766 > 172.18.0.2.443: Flags [S], ...
18:23:32.228271 IP 172.18.0.2.443 > 172.18.0.1.39766: Flags [S.], ...
...

The proxy netwok was created with docker network create proxy. proxy network details:

docker inspect proxy
[
    {
        "Name": "proxy",
        "Id": "40429cbcf263dcdb61fd0d23c50deecc8da3d6cacbb7a495813760277f0d1068",
        "Created": "2022-12-26T02:10:00.194178609Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "bfccdc8b4723920da92c3d6287a52c457457908bcf477d17b6776ad8ea77967e": {
                "Name": "traefik",
                "EndpointID": "7a632d50e2dab564156a2806eb6559c6f412ea47beaf2b4c6c71c71a2ca75cb6",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            },
            "c945edaea58a0331b8ff51b311c637303cfabc510192e10e8f28e6e7222e523a": {
                "Name": "portainer",
                "EndpointID": "044905b30cb22f1b841a03f8da6f41916807404f0a1cc6e7ae5ac72e8d4c3370",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }

Setup

Config files

docker-compose.yml

version: '3'

networks:
  proxy:
     external: true

services:
  traefik:
    image: "traefik:v2.10"
    container_name: "traefik"
    restart: always
    security_opt:
      - "no-new-privileges:true"
    ports:
      # use host mode to get the client IP https://dockerswarm.rocks/traefik/#getting-the-client-ip
      - target: 8080
        published: 8080
        mode: host
      - target: 80
        published: 80
        mode: host
      - target: 443
        published: 443
        mode: host
    cap_add:
      - NET_BIND_SERVICE
    networks:
      proxy:
        aliases:
          - traefik
    env_file:
      - .env
    volumes:
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
      - /run/user/1000/docker.sock:/var/run/docker.sock:ro
      - /home/portainer/container_configs/traefik/acme/acme.json:/acme.json
      - /home/portainer/container_configs/traefik/config/traefik.yml:/traefik.yml:ro
      - /home/portainer/container_configs/traefik/config/config.yml:/config.yml:ro
      - /home/portainer/container_configs/traefik/letsencrypt:/letsencrypt
      - /home/portainer/container_configs/traefik/ssl_certs:/ssl-certs

traefik.yml

global:
  checkNewVersion: true
  sendAnonymousUsage: false

log:
  level: DEBUG

api:
  insecure: true
  dashboard: true

experimental:
  plugins:
    GeoBlock:
      moduleName: "github.com/PascalMinder/geoblock"
      version: "v0.2.5"

entryPoints:
  web:
    address: :80
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https

  websecure:
    address: :443
    http:
      middlewares:
        - default-headers@file
        - my-GeoBlock@file
      tls:
        certResolver: cloudflare
        domains:
          - main: somedomain.com
            sans:
              - "*.somedomain.com"

# Allow communication with upstream service behind a self-signed cert
serversTransport:
  insecureSkipVerify: true

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false  # Default is true
  file:
    filename: /config.yml

certificatesResolvers:
  cloudflare:
    acme:
      email: email@somedomain.com
      storage: acme.json
      dnsChallenge:
        provider: cloudflare
        resolvers:
          - "1.1.1.1:53"
          - "1.0.0.1:53"

config.yml

http:
  routers:
    srv:
      entryPoints:
        - websecure
      rule: "Host(`srv.somedomain.com`)"
      tls:
        certResolver: cloudflare
      service: srv

  services:
    srv:
      loadBalancer:
        servers:
          - url: "https://192.168.2.38:55500"
        passHostHeader: true

  middlewares:
    my-GeoBlock:
      plugin:
        GeoBlock:
          allowLocalRequests: true
          logLocalRequests: true
          logAllowedRequests: true
          logApiRequests: true
          api: "https://get.geojs.io/v1/ip/country/{ip}"
          apiTimeoutMs: 1750
          cacheSize: 1000
          forceMonthlyUpdate: true
          allowUnknownCountries: false
          unknownCountryApiResponse: "nil"
          countries:
            - CH

    https-redirect:
      redirectScheme:
        scheme: https

    default-headers:
      headers:
        frameDeny: true
        #sslRedirect: true
        browserXssFilter: true
        contentTypeNosniff: true
        forceSTSHeader: true
        stsIncludeSubdomains: true
        stsPreload: true
        # disable Referer header
        referrerPolicy: false

Thanks

PascalMinder commented 1 year ago

I’m not sure how I can help you. I guess there must be a configuration error.