acouvreur / traefik-modsecurity-plugin

Traefik plugin to proxy requests to owasp/modsecurity-crs:apache container
https://plugins.traefik.io/plugins/628c9eadffc0cd18356a9799/modsecurity-plugin
Apache License 2.0
142 stars 25 forks source link

Feat: Perfomance (go http, docker alpine images, and caching layer) #17

Closed troyxmccall closed 1 year ago

troyxmccall commented 1 year ago

caching layer allows for custom turning so you can override defaults

      - traefik.http.middlewares.waf.plugin.traefik-modsecurity-plugin.cacheEnabled=true
      - traefik.http.middlewares.waf.plugin.traefik-modsecurity-plugin.cacheConditionsMethods=["GET"]
      - traefik.http.middlewares.waf.plugin.traefik-modsecurity-plugin.cacheConditionsNoBody=true
      - traefik.http.middlewares.waf.plugin.traefik-modsecurity-plugin.cacheKeyIncludeMethod=true
      - traefik.http.middlewares.waf.plugin.traefik-modsecurity-plugin.cacheKeyIncludeHost=true
      - traefik.http.middlewares.waf.plugin.traefik-modsecurity-plugin.cacheKeyIncludeRequestURI=true
      - traefik.http.middlewares.waf.plugin.traefik-modsecurity-plugin.cacheKeyIncludeHeaders=false
      - traefik.http.middlewares.waf.plugin.traefik-modsecurity-plugin.cacheKeyHeaders=["Authorization", "User-Agent", "Cache-Control"]
      - traefik.http.middlewares.waf.plugin.traefik-modsecurity-plugin.cacheKeyMatchAllHeaders=false
      - traefik.http.middlewares.waf.plugin.traefik-modsecurity-plugin.cacheKeyIncludeRemoteAddress=false

note: keeping tags from origin


some benchmarks on bare-metal

❯ neofetch
             .',;::::;,'.                tdmccall@fedora
         .';:cccccccccccc:;,.            ---------------
      .;cccccccccccccccccccccc;.         OS: Fedora Linux 37 (Workstation Edition) x86_64
    .:cccccccccccccccccccccccccc:.       Host: Alienware Area-51 R2
  .;ccccccccccccc;.:dddl:.;ccccccc;.     Kernel: 6.2.10-200.fc37.x86_64
 .:ccccccccccccc;OWMKOOXMWd;ccccccc:.    Uptime: 3 days, 5 hours, 46 mins
.:ccccccccccccc;KMMc;cc;xMMc:ccccccc:.   Packages: 1958 (rpm), 6 (flatpak)
,cccccccccccccc;MMM.;cc;;WW::cccccccc,   Shell: bash 5.2.15
:cccccccccccccc;MMM.;cccccccccccccccc:   Terminal: /dev/pts/0
:ccccccc;oxOOOo;MMM0OOk.;cccccccccccc:   CPU: Intel i7-5820K (12) @ 3.600GHz
cccccc:0MMKxdd:;MMMkddc.;cccccccccccc;   GPU: NVIDIA GeForce GT 710
ccccc:XM0';cccc;MMM.;cccccccccccccccc'   Memory: 926MiB / 15867MiB
ccccc;MMo;ccccc;MMW.;ccccccccccccccc;
ccccc;0MNc.ccc.xMMd:ccccccccccccccc;
cccccc;dNMWXXXWM0::cccccccccccccc:,
cccccccc;.:odl:.;cccccccccccccc:,.
:cccccccccccccccccccccccccccc:'.
.:cccccccccccccccccccccc:;,..
  '::cccccccccccccc::;,.

1) hitting whoami hooked directly into traefik (no middleware) - this is our benchmark

❯ docker run --rm -it --add-host=host.docker.internal:host-gateway alpine/bombardier host.docker.internal/bypass
Bombarding http://host.docker.internal:80/bypass for 10s using 125 connection(s)
[====================================================================================================================================================================================================================================================================================================================================================================] 10s
Done!
Statistics        Avg      Stdev        Max
  Reqs/sec     16570.33    1459.95   19404.05
  Latency        7.54ms     5.76ms   180.94ms
  HTTP codes:
    1xx - 0, 2xx - 165751, 3xx - 0, 4xx - 0, 5xx - 0
    others - 0
  Throughput:     8.69MB/s

2a) origin's response time with 200's (modsecurity layer activated)

  ❯ docker run --rm -it --add-host=host.docker.internal:host-gateway alpine/bombardier host.docker.internal/website Bombarding http://host.docker.internal:80/website for 10s using 125 connection(s)
[====================================================================================================================================================================================================================================================================================================================================================================] 10s
Done!
Statistics        Avg      Stdev        Max
  Reqs/sec      1978.29     396.11    2900.14
  Latency       62.97ms    43.64ms   515.52ms
  HTTP codes:
    1xx - 0, 2xx - 19890, 3xx - 0, 4xx - 0, 5xx - 0
    others - 0
  Throughput:     1.05MB/s

2b) origin's response time with 4xx ((modsecurity layer activated))

❯ docker run --rm -it --add-host=host.docker.internal:host-gateway alpine/bombardier host.docker.internal/website?test=../etc,
Bombarding http://host.docker.internal:80/website?test=../etc, for 10s using 125 connection(s)
[====================================================================================================================================================================================================================================================================================================================================================================] 10s

Done!
Statistics        Avg      Stdev        Max
  Reqs/sec      2006.71     253.99    3439.49
  Latency       62.13ms    23.07ms   270.12ms
  HTTP codes:
    1xx - 0, 2xx - 0, 3xx - 0, 4xx - 20174, 5xx - 0
    others - 0
  Throughput:   852.06KB/s

3a) our layer, just with alpine and http transport (no-caching) with 200's (modsecurity layer activated)

  ❯ docker run --rm -it --add-host=host.docker.internal:host-gateway alpine/bombardier host.docker.internal/website
Bombarding http://host.docker.internal:80/website for 10s using 125 connection(s)
[====================================================================================================================================================================================================================================================================================================================================================================] 10s
Done!
Statistics        Avg      Stdev        Max
  Reqs/sec      2519.61    1271.55    6695.99
  Latency       49.61ms    24.01ms   237.96ms
  HTTP codes:
    1xx - 0, 2xx - 25247, 3xx - 0, 4xx - 0, 5xx - 0
    others - 0
  Throughput:     1.33MB/s

3b) our layer, just with alpine and http transport (no-caching) with 4xx (modsecurity layer activated)

  ❯ docker run --rm -it --add-host=host.docker.internal:host-gateway alpine/bombardier host.docker.internal/website?test=../etc,
Bombarding http://host.docker.internal:80/website?test=../etc, for 10s using 125 connection(s)
[====================================================================================================================================================================================================================================================================================================================================================================] 10s
Done!
Statistics        Avg      Stdev        Max
  Reqs/sec      3205.40     364.19    4456.17
  Latency       38.92ms    22.37ms   348.71ms
  HTTP codes:
    1xx - 0, 2xx - 897, 3xx - 0, 4xx - 31279, 5xx - 0
    others - 0
  Throughput:     1.31MB/s

4a) our layer, with full caching, with 200's (modsecurity layer activated):

❯ docker run --rm -it --add-host=host.docker.internal:host-gateway alpine/bombardier host.docker.internal/website
Bombarding http://host.docker.internal:80/website for 10s using 125 connection(s)
[====================================================================================================================================================================================================================================================================================================================================================================] 10s
Done!
Statistics        Avg      Stdev        Max
  Reqs/sec      9850.41    1213.66   12773.77
  Latency       12.69ms     9.51ms   198.08ms
  HTTP codes:
    1xx - 0, 2xx - 98522, 3xx - 0, 4xx - 0, 5xx - 0
    others - 0
  Throughput:     5.20MB/s

4b) our layer, with full caching, with 4xx (modsecurity layer activated):

❯ docker run --rm -it --add-host=host.docker.internal:host-gateway alpine/bombardier host.docker.internal/website?test=../etc,
Bombarding http://host.docker.internal:80/website?test=../etc, for 10s using 125 connection(s)
[====================================================================================================================================================================================================================================================================================================================================================================] 10s
Done!
Statistics        Avg      Stdev        Max
  Reqs/sec     19179.62    2878.93   26408.90
  Latency        6.51ms     2.69ms   125.72ms
  HTTP codes:
    1xx - 0, 2xx - 0, 3xx - 0, 4xx - 191841, 5xx - 1
    others - 0
  Throughput:     3.59MB/s

rough summary compared to baseline:


2a. Origin's response time with 200s (ModSecurity activated)
Reqs/sec: -88.07%
Latency: +735.14%
Throughput: -87.93%

2b. Origin's response time with 4xx (ModSecurity activated)
Reqs/sec: -87.89%
Latency: +723.76%
Throughput: -90.19%

3a. Our layer, no caching, 200s (ModSecurity activated)
Reqs/sec: -84.80%
Latency: +557.97%
Throughput: -84.69%

3b. Our layer, no caching, 4xx (ModSecurity activated)
Reqs/sec: -80.65%
Latency: +416.05%
Throughput: -84.92%

4a. Our layer, full caching, 200s (ModSecurity activated)
Reqs/sec: -40.57%
Latency: +68.43%
Throughput: -40.18%

4b. Our layer, full caching, 4xx (ModSecurity activated)
Reqs/sec: +15.70%
Latency: -13.67%
Throughput: -58.67%
troyxmccall commented 1 year ago

omg, so sorry, i meant to make this on my own fork, it's late, i apologize

acouvreur commented 1 year ago

Haha! No issue @troyxmccall, I'd still take a look at the improvement you made.

Although you should never cache the Authroization header as someone else who's making a request might end up with someone else's Authorization header value. (Stealing the JWT of someone else...)

troyxmccall commented 1 year ago

@acouvreur excellent catch, ty! Will update my fork to have a blacklist of headers that can never be cached