Synit WAF

Beispielkonfigurationen

Kundensichere Beispielkonfigurationen für Synit WAF: Basic, Intermediate, Advanced, Optimized, Docker Swarm, API-Profil, WordPress-Profil und CRS-Grundeinstellungen.

Beispielkonfigurationen

Diese Beispiele sind Startpunkte für eigene Konfigurationen. Sie nutzen .example.test Domains und Platzhalter. Übernehmen Sie keine Secrets, Tokens, Passwort-Hashes oder internen Hostnamen in öffentliche Repositories.

Starten Sie neue Profile im Audit Mode. Aktivieren Sie Blocking erst, wenn Logs, False Positives, Rollback und Supportweg geprüft sind.

Basic: erste Website oder Portalstrecke

Datei: config.basic.yml

global_settings:
  log_level: "INFO"
  response_buffer_limit: 1048576
  read_timeout: "15s"
  write_timeout: "15s"
  idle_timeout: "60s"
  global_rate_limit:
    requests_per_minute: 2000
    burst: 500
  trust_forwarded_for: false

license:
  key: "<product-access-key>"
  server_url: "<validation-url-from-handoff>"

waf_rule_sets:
  "base-protection": |
    SecRule REQUEST_METHOD "@rx (?i:^(trace|track)$)" "id:110001,phase:1,deny,status:403,log,msg:'Blocked unsafe HTTP method'"
    SecRule ARGS|REQUEST_URI "@rx (?i:(\\bunion\\b.{0,20}\\bselect\\b|\\bor\\b\\s+1=1|\\bdrop\\b\\s+table\\b))" "id:110002,phase:2,deny,status:403,log,msg:'Blocked SQL injection pattern'"
    SecRule ARGS|REQUEST_URI "@rx (?i:(\\.\\./|%2e%2e%2f|%2e%2e/|/etc/passwd))" "id:110003,phase:2,deny,status:403,log,msg:'Blocked traversal pattern'"

tenants:
  "www.example.test":
    upstreams:
      - url: "http://website:8080"
    security:
      waf_enabled: true
      audit_mode: true
      paranoia_level: 1
      include_rule_sets:
        - "base-protection"

Intermediate: API plus CrowdSec

Datei: config.intermediate.yml

global_settings:
  log_level: "INFO"
  response_buffer_limit: 1048576
  read_timeout: "15s"
  write_timeout: "15s"
  idle_timeout: "60s"

license:
  key: "<product-access-key>"
  server_url: "<validation-url-from-handoff>"

crowdsec:
  api_url: "http://crowdsec:8080/"
  api_key: "<crowdsec-bouncer-key>"

waf_rule_sets:
  "base-protection": |
    SecRule REQUEST_METHOD "@rx (?i:^(trace|track)$)" "id:110001,phase:1,deny,status:403,log,msg:'Blocked unsafe HTTP method'"
    SecRule ARGS|REQUEST_URI "@rx (?i:(\\bunion\\b.{0,20}\\bselect\\b|\\bor\\b\\s+1=1|\\bdrop\\b\\s+table\\b))" "id:110002,phase:2,deny,status:403,log,msg:'Blocked SQL injection pattern'"
    SecRule ARGS|REQUEST_URI "@rx (?i:(\\.\\./|%2e%2e%2f|%2e%2e/|/etc/passwd))" "id:110003,phase:2,deny,status:403,log,msg:'Blocked traversal pattern'"
  "advanced-protection": |
    SecRule REQUEST_METHOD "@rx (?i:^(trace|track)$)" "id:120001,phase:1,deny,status:403,log,msg:'Blocked unsafe HTTP method'"
    SecRule ARGS|REQUEST_URI "@rx (?i:(\\bunion\\b.{0,20}\\bselect\\b|\\bor\\b\\s+1=1|\\bdrop\\b\\s+table\\b))" "id:120002,phase:2,deny,status:403,log,msg:'Blocked SQL injection pattern'"
    SecRule ARGS|REQUEST_URI "@rx (?i:(\\.\\./|%2e%2e%2f|%2e%2e/|/etc/passwd))" "id:120003,phase:2,deny,status:403,log,msg:'Blocked traversal pattern'"
    SecRule REQUEST_HEADERS:User-Agent "@rx (?i:(sqlmap|nikto|acunetix|nessus|masscan|nmap))" "id:120004,phase:1,deny,status:403,log,msg:'Blocked scanner user-agent'"

tenants:
  "api.example.test":
    upstreams:
      - url: "http://api-service:3000"
    security:
      waf_enabled: true
      audit_mode: true
      paranoia_level: 2
      crowdsec_enabled: true
      include_rule_sets:
        - "advanced-protection"

  "www.example.test":
    upstreams:
      - url: "http://frontend-service:80"
    security:
      waf_enabled: true
      audit_mode: true
      paranoia_level: 1
      crowdsec_enabled: true
      include_rule_sets:
        - "base-protection"
      custom_rules: |
        SecRule REQUEST_HEADERS:User-Agent "@contains BadBot/1.0" "id:220001,phase:1,deny,status:403,log,msg:'Blocked custom bad bot for website'"

Advanced: Adminbereich, GeoIP und Header

Datei: config.advanced.yml

global_settings:
  log_level: "DEBUG"
  response_buffer_limit: 2097152
  read_timeout: "20s"
  write_timeout: "20s"
  idle_timeout: "120s"
  global_rate_limit:
    requests_per_minute: 5000
    burst: 1000
  trust_forwarded_for: true
  ip_sets:
    allow_list:
      - "10.0.0.0/8"
    block_list:
      - "192.0.2.10/32"
  acme:
    enabled: true
    email: "security@example.test"
    storage_path: "/etc/waf/certs/acme"
    staging: true
    dns_provider: "cloudflare"
    dns_token: "<dns-api-token>"

logging:
  access_log:
    enabled: true
    path: "/var/log/waf/access-%yyyy-%MM-%dd.log"
    format: "json"
  error_log:
    enabled: true
    path: "/var/log/waf/error.log"
    format: "text"

license:
  key: "<product-access-key>"
  server_url: "<validation-url-from-handoff>"

crowdsec:
  api_url: "http://crowdsec:8080/"
  api_key: "<crowdsec-bouncer-key>"

geoip:
  db_path: "/etc/waf/geoip/GeoLite2-Country.mmdb"

waf_rule_sets:
  "optimized-protection": |
    SecRule REQUEST_METHOD "@rx (?i:^(trace|track)$)" "id:130001,phase:1,deny,status:403,log,msg:'Blocked unsafe HTTP method'"
    SecRule ARGS|REQUEST_URI "@rx (?i:(\\bunion\\b.{0,20}\\bselect\\b|\\bor\\b\\s+1=1|\\bdrop\\b\\s+table\\b|\\bwaitfor\\s+delay\\b))" "id:130002,phase:2,deny,status:403,log,msg:'Blocked SQL injection pattern'"
    SecRule ARGS|REQUEST_URI "@rx (?i:(\\.\\./|%2e%2e%2f|%2e%2e/|/etc/passwd|/proc/self/environ))" "id:130003,phase:2,deny,status:403,log,msg:'Blocked traversal pattern'"
    SecRule REQUEST_HEADERS:User-Agent "@rx (?i:(sqlmap|nikto|acunetix|nessus|masscan|nmap|zgrab|nuclei))" "id:130004,phase:1,deny,status:403,log,msg:'Blocked scanner user-agent'"
    SecRule REQUEST_URI "@rx (?i:(\\.env|\\.git/|composer\\.json|package-lock\\.json|backup\\.zip|dump\\.sql))" "id:130005,phase:2,deny,status:403,log,msg:'Blocked sensitive artifact access'"

tenants:
  "app.example.test":
    upstreams:
      - url: "http://app-service:80"
    upstreamInsecure: false
    header_transform:
      inject_request:
        X-Synit-Tenant: "{{TENANT}}"
      strip_response:
        - "Server"
        - "X-Powered-By"
    security:
      waf_enabled: true
      audit_mode: true
      circuit_breaker:
        enabled: true
        threshold: 5
        cooldown: "30s"
        failure_status_codes: [502, 503, 504]
      response_masking:
        - pattern: "(?i)(api[_-]?token=)[^&\\s]+"
          replacement: "$1[redacted]"
      block_page_url: "https://status.example.test/security-blocked"
      jwt_validation:
        enabled: true
        jwks_endpoint: "https://auth.example.test/.well-known/jwks.json"
      paranoia_level: 3
      crowdsec_enabled: true
      geoip_enabled: true
      blocked_countries: ["RU", "CN"]
      include_rule_sets:
        - "optimized-protection"

  "admin.example.test":
    upstreams:
      - url: "http://admin-service:80"
    security:
      waf_enabled: true
      audit_mode: true
      paranoia_level: 2
      crowdsec_enabled: true
      include_rule_sets:
        - "optimized-protection"
      basic_auth:
        - user: "ops"
          password: "<bcrypt-password-hash>"

  "*.customer.example.test":
    upstreams:
      - url: "http://customer-platform-ingress:80"
    security:
      waf_enabled: true
      audit_mode: true
      paranoia_level: 2
      crowdsec_enabled: true
      include_rule_sets:
        - "optimized-protection"

Optimized: getunte Produktion mit Geschäftsregeln

Datei: config.optimized.yml

global_settings:
  log_level: "INFO"
  response_buffer_limit: 4194304
  read_timeout: "20s"
  write_timeout: "20s"
  idle_timeout: "120s"

logging:
  access_log:
    enabled: true
    path: "/var/log/waf/access-%yyyy-%MM-%dd.log"
    format: "json"
  error_log:
    enabled: true
    path: "/var/log/waf/error-%yyyy-%MM-%dd.log"
    format: "json"

license:
  key: "<product-access-key>"
  server_url: "<validation-url-from-handoff>"

crowdsec:
  api_url: "http://crowdsec:8080/"
  api_key: "<crowdsec-bouncer-key>"

geoip:
  db_path: "/etc/waf/geoip/GeoLite2-Country.mmdb"

waf_rule_sets:
  "optimized-protection": |
    SecRule REQUEST_METHOD "@rx (?i:^(trace|track)$)" "id:130001,phase:1,deny,status:403,log,msg:'Blocked unsafe HTTP method'"
    SecRule ARGS|REQUEST_URI "@rx (?i:(\\bunion\\b.{0,20}\\bselect\\b|\\bor\\b\\s+1=1|\\bdrop\\b\\s+table\\b|\\bwaitfor\\s+delay\\b))" "id:130002,phase:2,deny,status:403,log,msg:'Blocked SQL injection pattern'"
    SecRule ARGS|REQUEST_URI "@rx (?i:(\\.\\./|%2e%2e%2f|%2e%2e/|/etc/passwd|/proc/self/environ))" "id:130003,phase:2,deny,status:403,log,msg:'Blocked traversal pattern'"
    SecRule REQUEST_HEADERS:User-Agent "@rx (?i:(sqlmap|nikto|acunetix|nessus|masscan|nmap|zgrab|nuclei))" "id:130004,phase:1,deny,status:403,log,msg:'Blocked scanner user-agent'"
    SecRule REQUEST_URI "@rx (?i:(\\.env|\\.git/|composer\\.json|package-lock\\.json|backup\\.zip|dump\\.sql))" "id:130005,phase:2,deny,status:403,log,msg:'Blocked sensitive artifact access'"
    SecRule REQUEST_URI|ARGS "@rx (?i:(169\\.254\\.169\\.254|metadata\\.google\\.internal))" "id:130006,phase:2,deny,status:403,log,msg:'Blocked metadata endpoint access pattern'"

  "business-core": |
    SecRule REQUEST_URI "@rx ^/v[0-9]+/" "id:240001,phase:1,pass,log,msg:'Versioned API route observed'"
    SecRule ARGS_NAMES "@rx (?i:(secret|token|api[_-]?key|private[_-]?key))" "id:240002,phase:2,deny,status:403,log,msg:'Sensitive argument name blocked'"

tenants:
  "api.example.test":
    upstreams:
      - url: "http://api-service-a:8080"
      - url: "http://api-service-b:8080"
    security:
      waf_enabled: true
      audit_mode: true
      paranoia_level: 2
      crowdsec_enabled: true
      geoip_enabled: true
      blocked_countries: ["RU", "CN"]
      include_rule_sets:
        - "optimized-protection"
        - "business-core"
      custom_rules: |
        SecRule REQUEST_HEADERS:X-Auth-Legacy "@rx .+" "id:240003,phase:1,deny,status:400,log,msg:'Deprecated auth header blocked'"

  "admin.example.test":
    upstreams:
      - url: "http://admin-service:8080"
    security:
      waf_enabled: true
      audit_mode: true
      paranoia_level: 3
      crowdsec_enabled: true
      geoip_enabled: true
      blocked_countries: ["RU", "CN"]
      include_rule_sets:
        - "optimized-protection"
      basic_auth:
        - user: "ops"
          password: "<bcrypt-password-hash>"

  "*.customer.example.test":
    upstreams:
      - url: "http://customer-ingress:80"
    security:
      waf_enabled: true
      audit_mode: true
      paranoia_level: 2
      crowdsec_enabled: true
      include_rule_sets:
        - "optimized-protection"

Docker Swarm

Datei: swarm-stack.yml

version: "3.9"

services:
  synit-waf:
    image: <container-image>
    environment:
      EDGE_WAF_CONFIG: /etc/waf/config.yml
    ports:
      - target: 80
        published: 80
        protocol: tcp
        mode: ingress
    volumes:
      - /etc/waf/config.yml:/etc/waf/config.yml:ro
      - /etc/waf/rules:/etc/waf/rules:ro
      - /etc/waf/geoip:/etc/waf/geoip:ro
      - /etc/waf/certs:/etc/waf/certs:ro
    networks:
      - edge
      - upstream
    deploy:
      replicas: 3
      endpoint_mode: vip
      update_config:
        parallelism: 1
        delay: 10s
        order: start-first
      rollback_config:
        parallelism: 1
        order: stop-first
      restart_policy:
        condition: on-failure
      resources:
        limits:
          cpus: "1.0"
          memory: 1024M
        reservations:
          cpus: "0.25"
          memory: 256M
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:80/healthz"]
      interval: 30s
      timeout: 5s
      retries: 3

  upstream-app:
    image: hashicorp/http-echo:1.0.0
    command: ["-listen=:5678", "-text=ok"]
    networks:
      - upstream
    deploy:
      replicas: 2

networks:
  edge:
    driver: overlay
    attachable: true
  upstream:
    driver: overlay
    attachable: true

Skalieren:

docker stack deploy -c swarm-stack.yml synit-waf-cluster
docker service scale synit-waf-cluster_synit-waf=5

API-Regelprofil

Datei: rules/profiles/api-strict.conf

SecRule REQUEST_METHOD "!@rx ^(GET|POST|PUT|DELETE|PATCH|OPTIONS)$" "id:210001,phase:1,deny,status:405,msg:'Method not allowed'"

SecRule REQUEST_METHOD "@rx ^(POST|PUT)$" "id:210002,phase:1,chain,deny,status:415,msg:'JSON Content-Type required'"
  SecRule REQUEST_HEADERS:Content-Type "!@contains application/json" "t:none"

SecRule REQUEST_URI "@rx \\.(env|git|bak|sql)$" "id:210003,phase:1,deny,status:403,msg:'Blocked sensitive file access'"

Dieses Profil passt für JSON-APIs. Prüfen Sie Uploads, Webhooks und Form-Posts vor Blocking.

WordPress-Regelprofil

Datei: rules/profiles/wordpress.conf

SecRule REQUEST_URI "@contains /wp-admin/" "id:200001,phase:1,pass,nolog,ctl:ruleRemoveById=942100"

SecRule REQUEST_URI "@contains wp-config.php" "id:200002,phase:1,deny,status:403,msg:'Blocked access to wp-config.php'"

SecRule REQUEST_URI "@contains xmlrpc.php" "id:200003,phase:1,deny,status:403,msg:'Blocked access to xmlrpc.php'"

Passen Sie WordPress-Ausnahmen eng an. Admin- und Editor-Workflows sollten vor produktivem Blocking getestet werden.

CRS-Grundeinstellungen

Datei: rules/crs-setup.local.conf

SecAction \
  "id:900000,\
  phase:1,\
  nolog,\
  pass,\
  t:none,\
  setvar:tx.paranoia_level=1"

SecAction \
  "id:900100,\
  phase:1,\
  nolog,\
  pass,\
  t:none,\
  setvar:tx.critical_anomaly_score=5,\
  setvar:tx.error_anomaly_score=4,\
  setvar:tx.warning_anomaly_score=3,\
  setvar:tx.notice_anomaly_score=2"

SecAction \
  "id:900200,\
  phase:1,\
  nolog,\
  pass,\
  t:none,\
  setvar:'tx.allowed_methods=GET HEAD POST OPTIONS'"

Paranoia-Level 1 ist ein sinnvoller Start. Höhere Level erhöhen Abdeckung und False-Positive-Risiko. Testen Sie sie zuerst in Staging oder mit einzelnen Tenants im Audit Mode.

United in Diversity