>

适用场景:家庭/小型办公/边缘集群,想把「被动挨打」变「主动防御」
> 三层体系:Fail2Ban(本地日志封禁) → CrowdSec(众包威胁情报+决策引擎) → Wazuh(HIDS/日志关联/合规审计)
> 部署形态:Docker Compose 编排、统一仪表盘、告警路由、自动响应、误封白名单


0. 为什么要三层?(单层都有盲区)

层级角色擅长盲区互补方式
Fail2Ban本地规则引擎解析任意日志格式、正则匹配、立即 iptables/nftables 封禁无威胁情报、无跨主机关联、规则手写维护兜底:应用层异常(如 404 扫描、API 暴力破解)第一时间封
CrowdSec众包决策引擎社区共享恶意 IP、现成场景、多 Bouncer(防火墙/HTTP/CDN)、自动拉取决策依赖社区规则覆盖面、本地日志解析需配置增强:把全网已知恶意 IP 同步到本地防火墙
WazuhHIDS + SIEM文件完整性监控 (FIM)、Rootkit 检测、日志关联、MITRE ATT&CK 映射、合规报告实时封禁能力弱、部署相对重深度:事后溯源、横向移动检测、合规审计、统一仪表盘
>

一句话:Fail2Ban 守门、CrowdSec 看黑名单、Wazuh 装监控摄像头+审计日志。


1. 架构总览(Docker Compose 编排)

+------------------+     +------------------+     +------------------+
|   Fail2Ban       |     |   CrowdSec       |     |   Wazuh          |
|   (容器)         |     |   (容器)         |     |   (Manager+Indexer|
|                  |     |                  |     |    +Dashboard)   |
| - sshd filter    |     | - acquis.yaml    |     |                  |
| - nginx filter   |     | - parsers/       |     | - FIM            |
| - custom filter  |     | - scenarios/     |     | - Rootcheck      |
| - banaction      |     | - bouncers/      |     | - Log Analysis   |
|   → nftables     |     |   → nftables     |     | - Compliance     |
+--------+---------+     +--------+---------+     +--------+---------+
         |                       |                       |
         |   共享网络 / 日志卷    |                       |
         +-----------+-----------+-----------+-----------+
                     |                       |
              +------v------+        +------v------+
              |  nftables   |        |  Elasticsearch|
              |  (宿主机)   |        |  (Indexer)    |
              +-------------+        +--------------+

2. 宿主机准备(所有节点跑一次)

# 1. 内核模块
modprobe nf_tables nf_conntrack nf_defrag_ipv4 nf_defrag_ipv6
echo -e "nf_tables\nnf_conntrack" > /etc/modules-load.d/firewall.conf

# 2. 目录
mkdir -p /opt/security/{fail2ban,crowdsec,wazuh}/{config,logs,data}

# 3. 宿主机 nftables 基础链(供 Bouncer 写入)
nft add table inet crowdsec
nft add chain inet crowdsec crowdsec-chain { type filter hook input priority 0 \; policy accept \; }
nft add chain inet crowdsec crowdsec6-chain { type filter hook input priority 0 \; policy accept \; }

3. Docker Compose 完整编排(/opt/security/docker-compose.yml)

version: "3.8"

networks:
  security:
    driver: bridge

volumes:
  crowdsec-config:
  crowdsec-data:
  wazuh-indexer-data:
  wazuh-manager-data:
  wazuh-dashboard-data:

services:
  # ==================== Fail2Ban ====================
  fail2ban:
    image: crazymax/fail2ban:latest
    container_name: fail2ban
    restart: unless-stopped
    network_mode: host          # 必须 host 网络才能操作宿主机 nftables
    cap_add:
      - NET_ADMIN
      - NET_RAW
    environment:
      - TZ=Asia/Shanghai
      - F2B_LOG_LEVEL=INFO
      - F2B_DB_PURGE_AGE=30d
    volumes:
      - /opt/security/fail2ban/config:/etc/fail2ban
      - /opt/security/fail2ban/logs:/var/log/fail2ban
      - /var/log:/host/var/log:ro      # 读取宿主机日志
      - /run/systemd/journal:/run/systemd/journal:ro  # systemd 日志
    # 配置文件见下文 3.1

  # ==================== CrowdSec ====================
  crowdsec:
    image: crowdsecurity/crowdsec:latest
    container_name: crowdsec
    restart: unless-stopped
    network_mode: host
    cap_add:
      - NET_ADMIN
      - SYS_PTRACE
    environment:
      - TZ=Asia/Shanghai
      - COLLECTIONS=crowdsecurity/sshd crowdsecurity/nginx crowdsecurity/iptables crowdsecurity/base-http-scenarios crowdsecurity/http-cve
      - PARSERS=crowdsecurity/sshd-logs crowdsecurity/nginx-logs crowdsecurity/syslog-logs
      - POSTOVERFLOWS=crowdsecurity/http-backdoors-attempts crowdsecurity/http-sensitive-files crowdsecurity/http-probing
    volumes:
      - /opt/security/crowdsec/config:/etc/crowdsec
      - /opt/security/crowdsec/data:/var/lib/crowdsec/data
      - /var/log:/host/var/log:ro
      - /run/systemd/journal:/run/systemd/journal:ro
    depends_on:
      - fail2ban

  # CrowdSec Bouncer:nftables(落地封禁)
  crowdsec-bouncer-nftables:
    image: crowdsecurity/crowdsec-bouncer-nftables:latest
    container_name: crowdsec-bouncer-nftables
    restart: unless-stopped
    network_mode: host
    cap_add:
      - NET_ADMIN
    environment:
      - CROWDSEC_AGENT_URL=http://localhost:8080
      - CROWDSEC_BOUNCER_API_KEY=${BOUNCER_API_KEY}  # 见下文生成
      - CROWDSEC_UPDATE_FREQUENCY=10s
      - CROWDSEC_NFTABLES_IPV4_TABLE=inet
      - CROWDSEC_NFTABLES_IPV4_CHAIN=crowdsec-chain
      - CROWDSEC_NFTABLES_IPV6_TABLE=inet
      - CROWDSEC_NFTABLES_IPV6_CHAIN=crowdsec6-chain
    depends_on:
      - crowdsec

  # ==================== Wazuh Manager ====================
  wazuh-manager:
    image: wazuh/wazuh-manager:4.9.0
    container_name: wazuh-manager
    restart: unless-stopped
    networks:
      - security
    ports:
      - "1514:1514/udp"   # Agent 通信
      - "1515:1515"       # Enrollment
      - "55000:55000"     # API
    environment:
      - TZ=Asia/Shanghai
      - WAZUH_MANAGER_NODE_NAME=manager-1
      - WAZUH_MANAGER_CLUSTER_NODES=manager-1
      - WAZUH_MANAGER_CLUSTER_KEY=ChangeMeClusterKey
      - WAZUH_REGISTRATION_PASSWORD=ChangeMeRegistrationPwd
      - WAZUH_API_USER=wazuh
      - WAZUH_API_PASSWORD=ChangeMeApiPwd
    volumes:
      - /opt/security/wazuh/config:/wazuh-config-mount
      - /opt/security/wazuh/data:/var/ossec/data
      - /opt/security/wazuh/logs:/var/ossec/logs
      - /var/log:/host/var/log:ro
    configs:
      - source: wazuh-manager-config
        target: /wazuh-config-mount/ossec.conf

  # ==================== Wazuh Indexer (Elasticsearch) ====================
  wazuh-indexer:
    image: wazuh/wazuh-indexer:4.9.0
    container_name: wazuh-indexer
    restart: unless-stopped
    networks:
      - security
    environment:
      - TZ=Asia/Shanghai
      - OPENSEARCH_JAVA_OPTS=-Xms2g -Xmx2g
      - WAZUH_INDEXER_NODE_NAME=indexer-1
      - WAZUH_INDEXER_CLUSTER_NODES=indexer-1
      - WAZUH_INDEXER_CLUSTER_KEY=ChangeMeClusterKey
      - WAZUH_INDEXER_PASSWORD=ChangeMeIndexerPwd
    volumes:
      - /opt/security/wazuh/indexer-data:/var/lib/wazuh-indexer
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536

  # ==================== Wazuh Dashboard ====================
  wazuh-dashboard:
    image: wazuh/wazuh-dashboard:4.9.0
    container_name: wazuh-dashboard
    restart: unless-stopped
    networks:
      - security
    ports:
      - "5601:5601"
    environment:
      - TZ=Asia/Shanghai
      - WAZUH_DASHBOARD_INDEXER_URL=https://wazuh-indexer:9200
      - WAZUH_DASHBOARD_MANAGER_URL=https://wazuh-manager:55000
      - WAZUH_DASHBOARD_PASSWORD=ChangeMeDashboardPwd
    volumes:
      - /opt/security/wazuh/dashboard-data:/usr/share/wazuh-dashboard/data
    depends_on:
      - wazuh-manager
      - wazuh-indexer

configs:
  wazuh-manager-config:
    file: ./wazuh-manager/ossec.conf

4. 关键配置文件(放在对应卷目录)

4.1 Fail2Ban:/opt/security/fail2ban/config/jail.d/custom.conf

[DEFAULT]
banaction = nftables-multiport
banaction_allports = nftables-allports
backend = systemd
findtime = 600
maxretry = 3
bantime = 86400
ignoreip = 127.0.0.1/8 ::1 192.168.10.0/24 10.10.10.0/24  # 管理网段白名单

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /host/var/log/auth.log
maxretry = 3
bantime = 86400

[nginx-http-auth]
enabled = true
filter = nginx-http-auth
logpath = /host/var/log/nginx/error.log
maxretry = 3

[nginx-botsearch]
enabled = true
filter = nginx-botsearch
logpath = /host/var/log/nginx/access.log
maxretry = 2
bantime = 604800

[nginx-badbots]
enabled = true
filter = nginx-badbots
logpath = /host/var/log/nginx/access.log
maxretry = 2

[recidive]
enabled = true
filter = recidive
logpath = /var/log/fail2ban.log
banaction = nftables-allports
bantime = 604800
findtime = 86400
maxretry = 5
>

自定义 filter 放 /opt/security/fail2ban/config/filter.d/,如 nginx-waf.conf、api-abuse.conf。

4.2 CrowdSec:/opt/security/crowdsec/config/acquis.yaml(关键:采集宿主机日志)

filenames:
  - /host/var/log/auth.log
  - /host/var/log/nginx/access.log
  - /host/var/log/nginx/error.log
  - /host/var/log/syslog
  - /host/var/log/kern.log
journalctl:
  - _SYSTEMD_UNIT=ssh.service
  - _SYSTEMD_UNIT=nginx.service
  - _SYSTEMD_UNIT=docker.service
>

安装完后进容器跑:cscli collections install crowdsecurity/sshd crowdsecurity/nginx crowdsecurity/base-http-scenarios
> cscli parsers install crowdsecurity/sshd-logs crowdsecurity/nginx-logs

4.3 CrowdSec Bouncer API Key 生成

# 在 crowdsec 容器内
docker exec -it crowdsec cscli bouncers add nftables-bouncer
# 输出 API Key,写入 .env 或直接填入 docker-compose.yml 的 BOUNCER_API_KEY

4.4 Wazuh Manager:/opt/security/wazuh/manager/ossec.conf(核心片段)

<ossec_config>
  <global>
    <json_output>yes</json_output>
    <alerts_log>yes</alerts_log>
    <logall>no</logall>
    <email_notification>no</email_notification>
  </global>

  <rules>
    <include>ruleset/rules/*.xml</include>
    <decoder_dir>ruleset/decoders</decoder_dir>
    <rule_dir>ruleset/rules</rule_dir>
    <list>etc/lists/audit-keys</list>
    <list>etc/lists/amazon/aws-eventnames</list>
  </rules>

  <syscheck>
    <disabled>no</disabled>
    <frequency>43200</frequency>
    <scan_on_start>yes</scan_on_start>
    <directories check_all="yes">/etc,/usr/bin,/usr/sbin,/bin,/sbin,/opt</directories>
    <ignore>/etc/mtab</ignore>
    <ignore>/etc/hosts.deny</ignore>
    <ignore>/etc/mail/statistics</ignore>
    <ignore>/etc/random-seed</ignore>
    <ignore>/etc/adjtime</ignore>
    <ignore>/etc/ld.so.cache</ignore>
    <nodiff>/etc/ssl/private</nodiff>
    <windows_registry>HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run</windows_registry>
    <windows_registry>HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce</windows_registry>
    <registry_ignore>HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run\WindowsDefender</registry_ignore>
    <registry_ignore>HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce\WindowsDefender</registry_ignore>
    <sync>yes</sync>
    <realtime>yes</realtime>
    <report_changes>yes</report_changes>
    <windows_audit_interval>3600</windows_audit_interval>
    <process_check>yes</process_check>
    <directories realtime="yes" check_all="yes">/etc,/usr/bin,/usr/sbin</directories>
  </syscheck>

  <rootcheck>
    <disabled>no</disabled>
    <check_dev>yes</check_dev>
    <check_files>yes</check_files>
    <check_if>yes</check_if>
    <check_pids>yes</check_pids>
    <check_ports>yes</check_ports>
    <check_unixaudit>yes</check_unixaudit>
    <readall>yes</readall>
  </rootcheck>

  <localfile>
    <log_format>syslog</log_format>
    <location>/host/var/log/auth.log</location>
  </localfile>
  <localfile>
    <log_format>syslog</log_format>
    <location>/host/var/log/nginx/access.log</location>
  </localfile>
  <localfile>
    <log_format>syslog</log_format>
    <location>/host/var/log/nginx/error.log</location>
  </localfile>
  <localfile>
    <log_format>syslog</log_format>
    <location>/host/var/log/syslog</location>
  </localfile>
  <localfile>
    <log_format>json</log_format>
    <location>/var/ossec/logs/active-responses.log</location>
  </localfile>

  <wodle name="command">
    <disabled>no</disabled>
    <tag>Wazuh-Command</tag>
    <command>/bin/bash /var/ossec/active-response/bin/firewall-drop.sh</command>
    <interval>0</interval>
    <ignore_output>no</ignore_output>
    <run_on_start>no</run_on_start>
  </wodle>

  <active_response>
    <disabled>no</disabled>
    <command>firewall-drop</command>
    <location>local</location>
    <rules_id>100200, 100201, 5710, 5712, 5714, 5716, 5720, 5722, 5724, 5726, 5728, 5730</rules_id>
    <timeout>600</timeout>
    <repeated_offenders>30,60,120,240,480,960,1920,3840,7680</repeated_offenders>
  </active_response>
</ossec_config>

5. 部署与验证

cd /opt/security
docker compose up -d

# 1. Fail2Ban
docker logs -f fail2ban
fail2ban-client status sshd

# 2. CrowdSec
docker exec -it crowdsec cscli decisions list
docker exec -it crowdsec cscli hub list
docker exec -it crowdsec cscli metrics

# 3. Bouncer 同步
nft list table inet crowdsec

# 4. Wazuh
# 浏览器打开 https://<IP>:5601
# 用户: wazuh / 密码: ChangeMeDashboardPwd
# 首次登录 → 添加 Agent → 复制注册命令 → 在被监控主机跑

6. Wazuh Agent 部署(被监控的每台主机)

# Ubuntu/Debian
curl -sO https://packages.wazuh.com/4.9/wazuh-agent_4.9.0-1_amd64.deb
sudo WAZUH_MANAGER=<MANAGER_IP> WAZUH_REGISTRATION_PASSWORD=ChangeMeRegistrationPwd dpkg -i wazuh-agent_4.9.0-1_amd64.deb
sudo systemctl enable --now wazuh-agent

# 验证
sudo /var/ossec/bin/agent_control -l
>

自动注册:Manager 配置 WAZUH_REGISTRATION_PASSWORD,Agent 安装时传同一密码即可自动入库。


7. 告警路由(统一推送到 Telegram/钉钉/企微/邮件)

7.1 CrowdSec → Webhook → Telegram

# /opt/security/crowdsec/config/notifications/telegram.yaml
type: http
name: telegram
format: |
  {
    "chat_id": "<CHAT_ID>",
    "text": "🚨 CrowdSec 告警\n决策: {{.Decision.Type}} {{.Decision.Value}}\n场景: {{.Decision.Scenario}}\n来源: {{.Decision.Source}}\n时间: {{.Decision.CreatedAt}}",
    "parse_mode": "HTML"
  }
url: "https://api.telegram.org/bot<BOT_TOKEN>/sendMessage"
method: POST
headers:
  Content-Type: "application/json"
docker exec -it crowdsec cscli notifications install /etc/crowdsec/notifications/telegram.yaml

7.2 Wazuh → 集成模块(原生支持)

<!-- ossec.conf 中添加 -->
<integration>
  <name>telegram</name>
  <hook_url>https://api.telegram.org/bot<BOT_TOKEN>/sendMessage</hook_url>
  <chat_id><CHAT_ID></chat_id>
  <level>7</level>
  <group>syscheck,rootcheck,authentication_failed</group>
  <alert_format>json</alert_format>
</integration>
>

也可走 Elasticsearch Watcher / Grafana Alerting 统一做二次加工。


8. 误封白名单 & 自动解封策略

场景处理方式
管理员 IP 被误封CrowdSec:cscli decisions delete -i <IP>;Fail2Ban:fail2ban-client set sshd unbanip <IP>;Wazuh:Dashboard → Active Response → Delete
固定白名单三层都加 ignoreip / whitelist / agentless_excluded_ips
自动解封Fail2Ban bantime / CrowdSec remediation 过期自动删 / Wazuh timeout 到期自动解除
持久化决策CrowdSec cscli decisions add --ip <IP> --type ban --duration -1(永久)

9. 统一仪表盘(Grafana 聚合)

# 数据源
# 1. CrowdSec:Prometheus metrics (crowdsec:6060/metrics) → Grafana Dashboard ID: 15753
# 2. Fail2Ban:node_exporter + fail2ban_exporter (https://github.com/fail2ban/fail2ban-exporter) → Dashboard ID: 12034
# 3. Wazuh:原生 Dashboard (5601) 或导出 Elasticsearch 到 Grafana

10. 维护清单

频率动作
每天cscli decisions list、fail2ban-client status、Wazuh Dashboard 告警巡检
每周cscli hub update && cscli hub upgrade、检查 nft list table inet crowdsec 规则数、Wazuh Agent 在线状态
每月Fail2Ban 规则审查(新增日志源)、CrowdSec 场景/解析器更新、Wazuh 规则集更新 (/var/ossec/bin/wazuh-update-rules)
每季度白名单复核、告警阈值调优、规则误报率统计、备份 Wazuh Indexer 快照

11. 常见坑 & 避坑指南

现象原因修正
CrowdSec 不封禁Bouncer 未运行 / API Key 错误 / acquis.yaml 路径不对docker logs crowdsec-bouncer-nftables、检查 cscli bouncers list
Fail2Ban 封不住 IPv6banaction 只配了 nftables-multiport(仅 v4)加 nftables-allports、确认 nft list table inet crowdsec 有 v6 链
Wazuh Agent 连不上 Manager1514/1515 端口未通 / 注册密码错 / 证书不信任tcpdump -i any port 1514、重新注册 agent-auth
规则过多导致 nftables 性能下降决策数 > 10 万CrowdSec 设置 max_decisions_per_bouncer、定期 cscli decisions prune
磁盘被 Wazuh Indexer 撑爆无 ILM 策略、副本数过多配置 Index Lifecycle Management:热/温/冷/删除、副本 1
误封导致服务不可用白名单未生效 / 规则过激分级告警:L1 只记录、L2 短时封、L3 永久封;核心服务 IP 强制白名单

12. 资源 & 参考


(完)

— 感谢阅读 —

一起交流

分享你的思考,让讨论更进一步。