Enabling a middleware for all Hosts matching a Regexp

I prepare a docker-compose-stack with traefik to proxy http(s) to various docker containers.

I use ACME/LetsEncrypt-Certs also. Things work so far.

Now we want to write a general rule saying:

"every Host matching dev.*.ci.tld.com should only be reachable from a specific ipallowlist"

I searched the web etc and already know that I should set up a router like:

http:
  routers:
    router-v3:
      entryPoints:
        - websecure
      rule: HostRegexp(dev\\.`[a-z]+\\.ci\\.tld\\.com`)
      ruleSyntax: v3
      middleware: ipallowlist2

Right?

I try to add that to the dynamic config of my container.
Is that correct?

Basically there are these 2 files (host-paths, files are mounted into /etc/traefik inside the container:

# cat traefik/config/traefik.yml 
api:
  dashboard: true
  insecure: true
  
  
entryPoints:
  ping:
    address: ':88'
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: ":443"
    http:
      #  middlewares:
      #    - traefik-crowdsec-bouncer@file
    proxyProtocol:
      trustedIPs:
       - 10.0.0.0/8
       - 172.16.0.0/12
       - 192.168.0.0/16
       - 62.40.xxx.yyy
    forwardedHeaders:
      trustedIPs:
       - 10.0.0.0/8
       - 172.16.0.0/12
       - 192.168.0.0/16
       - 62.40.xxx.yyy
  grafana:
    address: ":3000"
  grafana-secure:
    address: ":3001"
  portainer:
    address: ":9000"
  portainer-secure:
    address: ":9001"
  prometheus:
    address: ":9090"
  prometheus-secure:
    address: ":9091"
   
ping:
  entryPoint: "ping"
    
providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
    network: "nextcloud_proxy"
  file:
    directory: "./"
    watch: true
    
    
certificatesResolvers:
  http_resolver:
    acme:
      email: "administrator@tld.com"
      storage: "acme.json"
      httpChallenge:
        entryPoint: web
          # caServer: "https://acme-staging-v02.api.letsencrypt.org/directory"
  tls_resolver:
    acme:
      email: "administrator@tld.com"
      storage: "tls_letsencrypt.json"
      tlsChallenge: {} 
        
log:
  level: DEBUG

metrics:
  prometheus:
    addRoutersLabels: true
    buckets: "0.1,0.3,1.2,5.0"
  
accessLog: {}

(I know: one TLS-resolver is too much ... other topic)

and

# cat traefik/config/dynamic_conf.yml 
tls:
  options:
    default:
      minVersion: VersionTLS12
      cipherSuites:
        - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
        - TLS_AES_128_GCM_SHA256
        - TLS_AES_256_GCM_SHA384
        - TLS_CHACHA20_POLY1305_SHA256
      curvePreferences:
        - CurveP521
        - CurveP384
      sniStrict: true
http:
  #routers:
  #  router-v3:
  #    entryPoints:
  #      - websecure
  #    rule: HostRegexp(dev\\.`[a-z]+\\.ci\\.tld\\.com`)
  #    ruleSyntax: v3
  #    middleware: ipallowlist2
  middlewares:
    default:
      chain:
        middlewares:
          - default-security-headers
          - test-ipallowlist
            #- gzip
            #- test-ratelimit

    default-security-headers:
      headers:
        browserXssFilter: true
        contentTypeNosniff: true
        forceSTSHeader: true
        frameDeny: true
        stsIncludeSubdomains: true
        stsPreload: true
        stsSeconds: 31536000
        customFrameOptionsValue: "SAMEORIGIN"

    nextcloud-headers:
      headers:
        browserXssFilter: true                            # X-XSS-Protection=1; mode=block
        contentTypeNosniff: true                          # X-Content-Type-Options=nosniff
        forceSTSHeader: true                              # Add the Strict-Transport-Security header even when the connection is HTTP
        frameDeny: true                                   # X-Frame-Options=deny
        referrerPolicy: "strict-origin-when-cross-origin"
        stsIncludeSubdomains: true                        # Add includeSubdomains to the Strict-Transport-Security header
        stsPreload: true                                  # Add preload flag appended to the Strict-Transport-Security header
        stsSeconds: 63072000                              # Set the max-age of the Strict-Transport-Security header (63072000 = 2 years)
        customFrameOptionsValue: "SAMEORIGIN"

    gzip:
      compress: {}

    real-ip-cf:
      plugin:
        real-ip:
          Proxy:
            - proxyHeadername: "*"
              realIP: Cf-Connecting-Ip
              OverwriteXFF: true

    test-ipallowlist:
      ipAllowList:
        sourceRange:
[MASKED]

    ipallowlist2:
      ipAllowList:
        sourceRange:
[MASKED]

    test-ratelimit:
      rateLimit:
        average: 100
        burst: 50

Is the spot in the dynamic config the right place to work from?
If I uncomment the block traefik dashboard fails ... I still have to dig through all the logs to maybe spot the issue, sorry.

I just ask for a general direction here: where to place that rule, how to name/configure that router etc

thanks in advance!

You can assign a middleware to an entrypoint or router. Router will use rule to match incoming requests. As you stated, you can use a Regex on the router rule, but you can only assign a single target service to the router.

Thank you! So in my case this would go into traefik.yml, into the part defining the entrypoint websecure? I still don't understand how to place the rule with the HostRegexp there. Could you advise?

No, routers are defined in dynamic config.

You can assign a middleware to an entrypoint (listening port) for all routers using it (doc).

Note: you can’t really assign a global middleware to something like *.local.example.com (using multiple routers), if that is what you are looking for.

If I assign a middleware to an entrypoint can it still use that filtering via a HostRegexp rule?
thank you, sorry for being slow ..

If you assign the middleware to the entrypoint, then it will be applied to all routers using that entrypoint.

If you want a middleware to be assigned to a single router/service, then you should assign it to the router only.

If you have those on websecure entrypoint

www.example.com -> www target service
a.ci.example.com -> a target service
b.ci.example.com -> b target service (different than a)

then you need to create multiple routers for each ci with different target and assign middleware to every single one. (Maybe I am just too complicated :wink:)

Drifted off here, other topics. I think I will stay with labels in docker-compose. Thanks so far!