Traefik как WAF
WAF (Web Application Firewall) — это инструмент, который защищает веб-приложения от различных видов атак, анализируя и фильтруя HTTP-запросы. Вот основные преимущества и случаи, когда WAF может быть полезен:
Отдельным плюсом является возможность использования WAF без изменения кода приложения. WAF работает как промежуточное звено между пользователем и сервером, поэтому разработчикам не нужно вносить изменения в код приложения. Это экономит время и снижает вероятность ошибок.
Однако, библиотека про которую пойдет речь в этой статье, Coraza, также может быть использована внутри приложения.
Настройка WAF
Вы можете писать правила для WAF самостоятельно. Однако, это может быть сложно и требует определенных знаний в области безопасности. Поэтому, в большинстве случаев, проще использовать готовые правила, которые предоставляются поставщиком WAF. Вместе с Coraza, вы можете использовать правила из OWASP Core Rule Set. Он содержит набор правил для защиты от распространенных угроз.
- SQL Injection (SQLi) — передача в запросе SQL-код, выполняющийся впоследствии на сервере.
- Cross Site Scripting (XSS) — внедрение вредоносного кода на страницу, который выполнится в браузере пользователя.
- Local File Inclusion (LFI) — внедрение вредоносного кода в файлы на сервере.
- Remote File Inclusion (RFI) — внедрение вредоносного кода из внешнего источника.
- PHP Code Injection — внедрение PHP-кода в запрос.
- Java Code Injection — внедрение Java-кода в запрос.
- HTTPoxy — атака на сервер, использующая переменные окружения.
- Shellshock — атака на сервер, использующая уязвимость в bash.
- Unix/Windows Shell Injection — внедрение команд в запрос.
- Session Fixation — атака на сессию пользователя.
- Scanner/Bot Detection — обнаружение сканеров и ботов.
- Metadata/Error Leakages — обнаружение утечек метаданных и ошибок.
Установка Coraza
Coraza — это плагин для Traefik, который добавляет WAF-функционал.
Плагины в Traefik можно устанавливать динамически, просто указав их в конфигурации. А можно предварительно скачать плагин и указать локальный путь к нему. В этой статье мы рассмотрим второй вариант.
FROM alpine:3.21 AS build
ARG NAME=jcchavezs/coraza-http-wasm-traefik
ARG PLUGIN_MODULE=github.com/${NAME}
ARG ARTIFACT_NAME=coraza-http-wasm
ARG PLUGIN_VERSION=v0.3.0
RUN apk update && \
    apk add zip wget git && \
    mkdir -p /plugins-local/src/${PLUGIN_MODULE} && \
    wget https://${PLUGIN_MODULE}/releases/download/${PLUGIN_VERSION}/${ARTIFACT_NAME}-${PLUGIN_VERSION}.zip -O /tmp/plugin.zip && \
    unzip /tmp/plugin.zip -d /plugins-local/src/${PLUGIN_MODULE} && \
    wget https://raw.githubusercontent.com/${NAME}/refs/tags/${PLUGIN_VERSION}/.traefik.yml -O /plugins-local/src/${PLUGIN_MODULE}/.traefik.yml
RUN wget https://raw.githubusercontent.com/corazawaf/coraza/v3/dev/coraza.conf-recommended -O coraza.conf && \
    git clone https://github.com/coreruleset/coreruleset
FROM traefik:v3.3
COPY  /plugins-local /plugins-local
COPY  /coraza.conf /coraza/coraza.conf
COPY  /coreruleset /coreruleset
Настройка Coraza
Теперь, когда плагин установлен, нужно настроить его. Для этого создадим два файла конфигурации: статический и динамический.
api:
  insecure: true
entryPoints:
  web:
    address: :80
providers:
  file:
    filename: /etc/traefik/config-dynamic.yaml
log:
  level: ERROR
experimental:
  localPlugins:
    coraza:
      moduleName: github.com/jcchavezs/coraza-http-wasm-traefik
      settings:
        mounts:
          - /coraza:/coraza
          - /coreruleset:/coreruleset
Здесь, стоит обратить внимание на настройку settings.mounts в строках 19-22. Traefik ограничивает для плагина доступ
к файловой системе. Поэтому, если плагин должен читать файлы, их нужно предварительно примонтировать.
http:
  routers:
    httpbin:
      rule: PathPrefix(`/`)
      service: httpbin
      entryPoints:
        - web
      middlewares:
        - waf
  services:
    httpbin:
      loadBalancer:
        servers:
          - url: http://httpbin:8000
  middlewares:
    waf:
      plugin:
        coraza:
          directives:
            - Include /coraza/coraza.conf
            - Include /coreruleset/crs-setup.conf.example
            - Include /coreruleset/rules/*.conf
            - SecRuleUpdateTargetById 932130 "REQUEST_HEADERS"
            - SecRuleEngine On
В динамическом конфиге мы указываем правила, которые должен применить WAF. В данном случае, мы используем правила из OWASP Core Rule Set.
Запуск
Теперь, когда все настроено, можно запустить Traefik с плагином Coraza.
Для этого, я предлагаю использовать Docker-compose. Создайте файл docker-compose.yml и добавьте следующий код:
version: '3.7'
services:
  traefik:
    build:
        context: .
        dockerfile: Dockerfile
    ports:
      - 8080:80
    command:
      - "--configFile=/etc/traefik/config-static.yaml"
    volumes:
      - ./:/etc/traefik
    depends_on:
      - httpbin
  httpbin:
    image: mccutchen/go-httpbin:v2.9.0
    environment:
      - APP_NAME=httpbin
      - MAX_BODY_SIZE=15728640 # 15 MiB
    command: [ "/bin/go-httpbin", "-port", "8000" ]
    ports:
      - 8000:8000
Обратите внимание, что доступны оба контейнера и Traefik, и httpbin. Таким образом мы можем проверить, как работает WAF.
Для этого мы можем отправить запрос на httpbin, который содержит SQL-инъекцию. Например:
curl "http://localhost:8000/post" -i -XPOST -d"id=1%3B Drop TABLE users"
В ответ мы получим:
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Content-Type: application/json; encoding=utf-8
Date: Fri, 24 Jan 2025 17:02:06 GMT
Content-Length: 501
{
  "args": {},
  "headers": {
    "Accept": [
      "*/*"
    ],
    "Content-Length": [
      "24"
    ],
    "Content-Type": [
      "application/x-www-form-urlencoded"
    ],
    "Host": [
      "localhost:8000"
    ],
    "User-Agent": [
      "curl/8.7.1"
    ]
  },
  "method": "POST",
  "origin": "192.168.97.1:50006",
  "url": "http://localhost:8000/post",
  "data": "id=1%3B Drop TABLE users",
  "files": null,
  "form": {
    "id": [
      "1; Drop TABLE users"
    ]
  },
  "json": null
}
Видно, что данные с SQL-инъекцией были обработаны HTTPbin. Т.е., если бы мы пробросили данные из запроса в базу данных,
то таблица users была бы удалена.
Теперь давайте отправим такой же запрос, но через Traefik:
curl "http://localhost:8080/post" -i -XPOST -d"id=1%3B Drop TABLE users"
В ответ мы получим:
HTTP/1.1 403 Forbidden
Date: Fri, 24 Jan 2025 17:05:22 GMT
Content-Length: 0
А в логах Traefik:
ERR [client "192.168.97.1"] Coraza: Warning. SQL Injection Attack Detected via libinjection [file "/coreruleset/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf"] [line "8813"] [id "942100"] [rev ""] [msg "SQL Injection Attack Detected via libinjection"] [data "Matched Data: 1;Tnn found within ARGS:id: 1; Drop TABLE users"] [severity "critical"] [ver "OWASP_CRS/4.11.0-dev"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-sqli"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "capec/1000/152/248/66"] [tag "PCI/6.5.2"] [hostname ""] [uri "/post"] [unique_id "JieKvlSfwSgKEuMGxSH"] entryPointName=web middlewareName=waf@file middlewareType=wasm routerName=httpbin@file
ERR [client "192.168.97.1"] Coraza: Warning. Detects MySQL UDF injection and other data/structure manipulation attempts [file "/coreruleset/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf"] [line "9197"] [id "942350"] [rev ""] [msg "Detects MySQL UDF injection and other data/structure manipulation attempts"] [data "Matched Data: ; Drop TABLE found within ARGS:id: 1; Drop TABLE users"] [severity "critical"] [ver "OWASP_CRS/4.11.0-dev"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-sqli"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "capec/1000/152/248/66"] [tag "PCI/6.5.2"] [hostname ""] [uri "/post"] [unique_id "JieKvlSfwSgKEuMGxSH"] entryPointName=web middlewareName=waf@file middlewareType=wasm routerName=httpbin@file
ERR [client "192.168.97.1"] Coraza: Warning. Detects concatenated basic SQL injection and SQLLFI attempts [file "/coreruleset/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf"] [line "9236"] [id "942360"] [rev ""] [msg "Detects concatenated basic SQL injection and SQLLFI attempts"] [data "Matched Data: 1; Drop TABLE found within ARGS:id: 1; Drop TABLE users"] [severity "critical"] [ver "OWASP_CRS/4.11.0-dev"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-sqli"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "capec/1000/152/248/66"] [tag "PCI/6.5.2"] [hostname ""] [uri "/post"] [unique_id "JieKvlSfwSgKEuMGxSH"] entryPointName=web middlewareName=waf@file middlewareType=wasm routerName=httpbin@file
ERR [client "192.168.97.1"] Coraza: Access denied (phase 2). Inbound Anomaly Score Exceeded (Total Score: 15) [file "/coreruleset/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "11553"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 15)"] [data ""] [severity "emergency"] [ver "OWASP_CRS/4.11.0-dev"] [maturity "0"] [accuracy "0"] [tag "anomaly-evaluation"] [tag "OWASP_CRS"] [hostname ""] [uri "/post"] [unique_id "JieKvlSfwSgKEuMGxSH"] entryPointName=web middlewareName=waf@file middlewareType=wasm routerName=httpbin@file
Таким образом, WAF успешно обнаружил и заблокировал SQL-инъекцию.
Вот еще несколько примеров запросов, которые можно отправить через Traefik, чтобы проверить работу WAF:
- 
Remote Command Execution: Unix Shell Code Found curl "http://localhost:8080/anything?foo=/etc/passwd&bar=/bin/sh" -i
- 
Path Traversal Attack curl "http://localhost:8080/anything?foo=../../../../etc/passwd" -i
- 
SQL Injection Attack Detected via libinjection curl "http://localhost:8080/anything?foo=1%27%20OR%20%271%27%3D%271" -i