Block direct connections to IP

Hi Traefik community,

I’m running Traefik to route my Docker containers on the internet, using Let’s Encrypt certificates, and it works perfectly.

However, I need to block direct connections to my server IP (http://IP or https://IP). For now, Traefik generate a “TRAEFIK DEFAULT CERT” instead of returning a timeout or “acces denied”.

I tried several approaches, but nothing worked:

  1. Using a catch-all router:
routers:
  catchall-router:
    rule: "HostRegexp({catchall:.*})"
    priority: 1
    service: unavailable-service
    entryPoints:
      - web
      - websecure
    tls: {}
  1. Using Nginx as a reverse proxy before Docker/Traefik.
  2. Blocking with iptables:
    iptables -I INPUT -d myIP -p tcp --dport 80 -j REJECT --reject-with tcp-reset
    iptables -I DOCKER-USER -p tcp -d myIP --dport 80 -j DROP

I spent many hours experimenting with AI tools, rewriting my traefik.yml and dynamic.yml, but nothing works. I also searched extensively on Google and this forum without success.

This is quite critical, as my server is hosted by my university, and the security manager will shut it down if I can’t fix this issue.

Here are my initial configuration files. My skills and knowledge of Traefik and IT security are limited, so any guidance would be greatly appreciated.

docker compose

services:
  traefik:
    image: traefik:v3.3
    # https://hub.docker.com/_/traefik
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    command:
      - "--log.filepath=/var/log/traefik.log"
      - "--log.level=ERROR"
      - "--accesslog=true"
      - "--accesslog.filepath=/var/log/access.log"
    networks:
      - frontend
    ports:
      - 80:80
      - 443:443
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik-data/traefik.yml:/traefik.yml:ro
      - ./traefik-data/acme.json:/acme.json
      - ./traefik-data/configurations:/configurations
      - ./traefik-data/logs:/var/log
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=frontend"
      - "traefik.http.routers.traefik-secure.entrypoints=websecure"
      - "traefik.http.routers.traefik-secure.rule=Host(`traefik.lora-laguette.fr`)"
      - "traefik.http.routers.traefik-secure.middlewares=user-auth@file"
      - "traefik.http.routers.traefik-secure.service=api@internal"

  portainer:
    image: portainer/portainer-ce:2.21.1-alpine
    # https://hub.docker.com/r/portainer/portainer-ce/tags
    container_name: portainer
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - frontend
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./portainer-data:/data
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=frontend"
      - "traefik.http.routers.portainer-secure.entrypoints=websecure"
      - "traefik.http.routers.portainer-secure.rule=Host(`portainer.lora-laguette.fr`)"
      - "traefik.http.routers.portainer-secure.service=portainer"
      - "traefik.http.services.portainer.loadbalancer.server.port=9000"


networks:
  frontend:
    external: true

traefik.yml

api:
  dashboard: true

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

  websecure:
    address: :443
    http:
      middlewares:
        - secureHeaders@file
      tls:
        certResolver: letsencrypt

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
  file:
    filename: /configurations/dynamic.yml

certificatesResolvers:
  letsencrypt:
    acme:
      email: nicolas-deschamps@cnrs-orleans.fr
      storage: acme.json
      keyType: EC384
      httpChallenge:
        entryPoint: web


dynamic.yml

# Dynamic configuration
http:
  middlewares:
    secureHeaders:
      headers:
        forceSTSHeader: true
        stsIncludeSubdomains: true
        stsPreload: true
        stsSeconds: 31536000

    user-auth:
      basicAuth:
        users:
          - "admin:$apr1$twuWd1TV$JexTfR9Le7km/hcM1hbjd."

  routers:
    http-challenge:
      rule: PathPrefix(`/.well-known/acme-challenge/`)
      entryPoints:
        - web
      priority: 100
      service: noop
      middlewares: []

  services:
    noop:
      loadBalancer:
        servers:
          - url: http://127.0.0.1

tls:
  options:
    default:
      cipherSuites:
        - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
        - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
      minVersion: VersionTLS11

What do you want to achieve? Any server is listening on IP addresses. DNS enables to use a domain name, but the browser will lookup the according IP address and connect to the IP. So you can't block "IP based access" and only allow "domains".

Thank you for your response.
The main issue is the self-signed certificate associated with TCP/443 on my base IP. This triggers alarms in the network security system…

You could set a default LetsEncrypt cert for all requests (doc), then it's not self-signed:

tls:
  stores:
    default:
      defaultGeneratedCert:
        resolver: myresolver
        domain:
          main: example.org
1 Like

Thanks a lot, that was the solution !
(inside dynamic.yml file)

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.