Search code examples
docker-swarmtraefikddoshetzner-cloud

Docker Swarm + Traefik under DDoS attack


env:

  • Docker Swarm
  • traefik
  • several services for listening 80 and 443 traffic in xxx.domain.com, yyy.domain.com and etc
  • Hetzner hosting

Domains have anti DDoS protection like Cloudflare But we have attacks by IP address and domain https://static.IP.clients.your-server.de (seems it is provided by default from Hetzner)

It is attack on 404 page - because we don't have service:

version: '3.3'

services:
  traefik:
    image: traefik:v2.10.6
    ports:
      - 80:80
      - 443:443
    deploy:
      placement:
        constraints:
          - node.labels.traefik-public.traefik-public-certificates == true
      labels:
        - traefik.enable=true
        - traefik.docker.network=traefik-public
        - traefik.constraint-label=traefik-public
        - traefik.http.middlewares.https-redirect.redirectscheme.scheme=https
        - traefik.http.middlewares.https-redirect.redirectscheme.permanent=true
        - traefik.http.routers.traefik-public-http.rule=Host(`${DOMAIN?Variable not set}`)
        - traefik.http.routers.traefik-public-http.entrypoints=http
        - traefik.http.routers.traefik-public-http.middlewares=https-redirect
        - traefik.http.routers.traefik-public-https.rule=Host(`${DOMAIN?Variable not set}`)
        - traefik.http.routers.traefik-public-https.entrypoints=https
        - traefik.http.routers.traefik-public-https.tls=true
        - traefik.http.routers.traefik-public-https.service=api@internal
        - traefik.http.routers.traefik-public-https.tls.certresolver=le
        - traefik.http.routers.traefik-public-https.middlewares=admin-auth
        - traefik.http.services.traefik-public.loadbalancer.server.port=8080



    volumes:
      # Add Docker as a mounted volume, so that Traefik can read the labels of other services
      - /var/run/docker.sock:/var/run/docker.sock:ro
      # Mount the volume to store the certificates
      - traefik-public-certificates:/certificates
    command:
      # Enable Docker in Traefik, so that it reads labels from Docker services
      - --providers.docker
      # Add a constraint to only use services with the label "traefik.constraint-label=traefik-public"
      - --providers.docker.constraints=Label(`traefik.constraint-label`, `traefik-public`)
      # Do not expose all Docker services, only the ones explicitly exposed
      - --providers.docker.exposedbydefault=false
      # Enable Docker Swarm mode
      - --providers.docker.swarmmode
      # Create an entrypoint "http" listening on port 80
      - --entrypoints.http.address=:80
      # Create an entrypoint "https" listening on port 443
      - --entrypoints.https.address=:443
      # Create the certificate resolver "le" for Let's Encrypt, uses the environment variable EMAIL
      - --certificatesresolvers.le.acme.email=${EMAIL?Variable not set}
      # Store the Let's Encrypt certificates in the mounted volume
      - --certificatesresolvers.le.acme.storage=/certificates/acme.json
      # Use the TLS Challenge for Let's Encrypt
      - --certificatesresolvers.le.acme.tlschallenge=true
      # Enable the access log, with HTTP requests
      - --accesslog
      # Enable the Traefik log, for configurations and errors
      - --log
      # Enable the Dashboard and API
      - --api
    networks:
      # Use the public network created to be shared between Traefik and
      # any other service that needs to be publicly available with HTTPS
      - traefik-public

volumes:
  # Create a volume to store the certificates, there is a constraint to make sure
  # Traefik is always deployed to the same Docker node with the same volume containing
  # the HTTPS certificates
  traefik-public-certificates:

networks:
  # Use the previously created public network "traefik-public", shared with other
  # services that need to be publicly available via this Traefik
  traefik-public:
    external: true

We can't hide our real server IP - so we have attack by IP.

Is it possible to accept requests only for domain.com and ***.domain.com?

We do not need 404 error on requests by IP - because DDoS is also possible with 404 error too


Solution

  • There's no much info but I will share my 2 cents here.

    • Talk with your hosting company to explore techniques to block requests from IP addresses from all countries except the one or ones you have business with, since our domain ends in DE I assume can just allow requests from DE.
    • As an alternative I would try with traefik this: https://plugins.traefik.io/plugins/62947313ffc0cd18356a97ca/geo-block after making all services private except for traefik. This sounds super easy so I really hope this works.
    • You need to put all your services behind a DMZ, they won't have a public IP again, create a New LoadBalancer (nginx, or something provided by your cloud provider), the LoadBalancer's public IP needs to be associated to all the domains and subdomains you need, this can be done in your DNS Manager.
    • Then in the configuration of your LoadBalancer(not sure if you can do this in Traefik) you need to check for the VHOST (domain or subdomain) and redirect those requests to the services that you previously made internal.

    If you pick something like NGINX the config look something like this:

    server {
    listen 80;
       root /var/www/html/example1.com;
       index index.html;
       server_name example1.com;
       location / {
           try_files $uri $uri/ =404;
       }
    }
    
    server {
       listen 80;
       root /var/www/html/example2.com;
       index index.html;
       server_name example2.com;
       location / {
           try_files $uri $uri/ =404;
       }
    }
    

    I took the snippet from here: https://fedingo.com/how-to-host-multiple-domains-on-one-server-in-nginx/