中小企业零成本开源安全闭环|全套可落地实操部署方案
适配前文博文架构|100%开源|低配服务器可跑|全自动安全闭环|自建IP信誉系统
本方案基于前文完整的开源安全体系,提供可直接复制部署、生产可用、无冗余、避坑优化的实操步骤。全程无需商业软件、无需大数据集群、无需专职安全运维,单台4C8G/4H8G虚拟机即可承载所有组件,完美适配中小企业预算少、人手缺、求稳定的核心需求。
部署整体顺序(核心原则:先采集、后研判、再处置)
系统环境准备 → 日志集中采集(Graylog)→ 全网风险感知(HFish+Suricata+Wazuh)→ 自建轻量化IP信誉系统 → 自动化封禁调度(SecAutoBan)→ 设备联动下发(防火墙/路由器ACL)→ 告警降噪+合规审计 → 常态化运维调优
一、基础环境标准化准备(生产必做)
1.1 硬件与系统要求
-
服务器配置:最低4C8G、40G固态磁盘(优先SSD,保障日志读写稳定)
-
操作系统:Ubuntu 22.04 / Debian 12(长期支持、兼容性最强)
-
环境要求:关闭Swap、开启时间同步、固定静态IP、关闭无用系统服务
1.2 系统初始化命令(可直接复制)
# 更新系统依赖
apt update && apt upgrade -y
# 安装基础必备组件
apt install -y python3 python3-pip git ipset iptables curl wget unzip supervisor cron openssh-server
# 关闭swap(永久生效)
swapoff -a
sed -i '/swap/d' /etc/fstab
# 时区同步
timedatectl set-timezone Asia/Shanghai
apt install -y systemd-timesyncd
systemctl enable --now systemd-timesyncd
二、第一层能力:全网日志归一化采集(Graylog)
核心作用:收敛防火墙、WAF、路由器、IDS、服务器、蜜罐所有日志,完成字段归一化、攻击IP聚合筛选,是整个安全体系的数据源底座。
2.1 极简部署方式(Docker一键部署)
采用官方稳定镜像,规避编译报错、依赖冲突问题,中小企业首选。
2.2 核心生产配置(关键避坑)
-
开启字段归一化Pipeline:统一所有设备源IP为
src_ip,解决多设备字段不统一无法聚合的问题,Pipeline规则沿用博文标准规则; -
配置日志生命周期:设置日志留存6个月(满足等保),自动清理过期日志,防止磁盘占满;
-
关闭无用日志采集:过滤内网正常访问、重复冗余日志,提前降噪,避免后续告警泛滥;
-
创建专属安全数据流:单独创建Security数据流,收纳所有安全设备、蜜罐、主机安全日志,与业务日志隔离。
2.3 可疑IP筛选定时任务(准实时落地)
放弃Graylog原生不稳定Webhook,部署前文提供的 graylog_fetch_suspicious_ip.py定时脚本,配置5分钟执行一次。
筛选阈值(生产最优配置):5分钟内同一src_ip攻击行为≥3次,判定为可疑IP,自动推送至后续处置服务,杜绝单条日志误判。
三、第二层能力:全网风险感知部署(三层主动探测)
补齐网络、主机、蜜罐三大感知维度,实现全方位攻击捕获,为IP信誉系统提供内部高权重真实攻击数据。
3.1 HFish开源蜜罐(最高可信度攻击源)
部署模式:公网节点+内网双节点部署
-
公网蜜罐:模拟SSH、FTP、MySQL、Web服务,捕获外网扫描、爆破攻击;
-
内网蜜罐:捕获内网横向移动、入侵渗透、内网爆破行为(内网告警优先级最高)。
关键配置:开启Webhook推送,将所有蜜罐攻击事件推送至本地统一调度接口,不直接封禁,进入信誉研判流水线。
3.2 Suricata开源IDS(旁路入侵检测)
核心优势:旁路镜像部署,不串接业务、无业务中断风险,零故障率。
-
加载ET Open免费开源规则集,检测端口扫描、漏洞探测、恶意请求、暴力破解;
-
所有告警日志以GELF格式推送至Graylog,统一聚合分析;
-
仅做检测告警,不主动阻断,保障业务稳定。
3.3 Wazuh主机安全监控(主机层防护)
全网所有Linux/Windows业务服务器部署Wazuh轻量Agent,核心采集:
-
系统账号变更、定时任务篡改、关键文件修改;
-
SSH、RDP暴力破解、异常登录行为;
-
主机恶意进程、病毒木马行为。
所有主机安全日志统一上报Graylog,暴力破解IP纳入可疑IP筛选池,异常篡改行为仅钉钉告警、不自动处置。
四、第三层能力:自建轻量化IP信誉系统(体系核心)
替换单一Maltrail情报核验,实现内外结合打分、分级研判,彻底解决误封、漏封问题,中小企业专属轻量架构(Redis+SQLite,无需大数据组件)。
4.1 数据源接入配置
-
外部开源情报定时同步:定时拉取Maltrail、FireHOL、Spamhaus恶意IP网段、AbuseIPDB免费情报,多源交叉验证;
-
内部数据全量接入:同步HFish蜜罐记录、Suricata告警、Wazuh主机攻击、Graylog聚合攻击日志、历史封禁记录。
4.2 打分模型落地配置(直接套用)
-
内部攻击行为高权重加分,外部情报低权重辅助加分;
-
全局白名单强制优先:内网、运维IP、CDN、云厂商网段、合作客户IP永久可信;
-
时间衰减机制:7天分数衰减20%,30天无攻击自动清零,规避IP复用误判。
4.3 分级处置规则(生产强制标准)
-
0-20分(可信):直接放行;
-
21-50分(可疑):仅钉钉告警、日志记录,禁止自动封禁;
-
51-80分(高风险):TTL限时8小时封禁;
-
81-100分(极高风险):全网封禁+高危钉钉@全员告警。
4.4 接口对接改造
修改SecAutoBan钩子脚本,废弃单一Maltrail判定,优先调用自建IP信誉接口,实现精准分级研判。
五、第四层能力:自动化封禁调度与设备联动
以SecAutoBan为执行中枢,实现本机+硬件设备双层封禁、自动解封、无人工干预。
5.1 SecAutoBan+Maltrail基础部署
沿用前文Docker Compose一键部署方案,统一配置:
-
封禁默认TTL 8小时,禁止永久封禁;
-
全局唯一白名单,所有组件共用,避免策略冲突;
-
开启钉钉分级告警,关闭冗余日志推送。
5.2 统一调度服务改造(hfish-push)
改造Flask调度服务,双接口接收:HFish蜜罐事件、Graylog可疑IP事件,所有攻击IP统一送入SecAutoBan研判流水线。
5.3 硬件设备联动(核心落地点)
新增设备上下线钩子脚本,实现封禁/解封双向联动:
-
支持华为、H3C、普通路由器SSH协议下发ACL黑名单;
-
跟随SecAutoBan TTL策略,到期自动删除硬件设备黑名单,杜绝永久封禁;
-
设备联动异常降级:硬件设备离线时,仅保留本机ipset封禁,不影响整体体系运行。
六、第五层能力:告警降噪与合规审计闭环
6.1 告警分级降噪
-
P0高危:内网蜜罐捕获、主机文件篡改、大规模爆破 → 钉钉@全员告警;
-
P1中危:外网高风险IP封禁、多次Web攻击 → 普通钉钉推送;
-
P2低危:单次扫描、偶发探测 → 仅日志留存,不推送告警。
6.2 全链路审计闭环
-
所有攻击、研判、封禁、解封、告警事件全部回写Graylog;
-
搭建Graylog安全态势仪表盘,直观展示TOP攻击IP、封禁统计、攻击类型分布;
-
配置定时脚本,每周自动生成安全简报,钉钉推送运维复盘。
七、事前加固配套部署(从源头减少攻击面)
自动化是事后处置,事前加固才是安全根本,中小企业轻量化落地:
-
自动备份系统:部署BorgBackup定时增量备份,保障勒索病毒、入侵兜底恢复能力;
-
基线定时检查:开源OpenSCAP定期扫描服务器配置、密码策略、权限合规性;
-
资产监控:定时脚本探测公网端口、子域名变更、证书到期,异常自动告警。
八、生产核心防护机制(零误封、高稳定)
-
全链路安全降级:任意组件故障(Graylog离线、情报服务异常、设备失联),系统自动停止自动封禁,仅保留日志,不影响业务;
-
禁止盲自动化:仅网络攻击、扫描、爆破支持自动处置,漏洞、配置、账号风险仅人工修复;
-
内部数据优先:自身环境真实攻击权重高于所有开源情报,适配自有业务场景;
-
无永久黑名单:所有封禁均带TTL时效,配合时间衰减,彻底解决IP复用误判问题。
九、落地部署顺序(7天落地计划表)
-
第1天:系统初始化、部署Graylog、完成日志归一化配置;
-
第2天:部署HFish蜜罐、Suricata旁路IDS,打通日志上报;
-
第3天:全网部署Wazuh Agent,补齐主机安全采集;
-
第4天:部署SecAutoBan+Maltrail,调试基础封禁能力;
-
第5天:搭建自建IP信誉系统,调试打分、分级规则;
-
第6天:调试设备联动封禁、告警降噪、审计回写;
-
第7天:整体联调、模拟攻击测试、策略优化、常态化定时任务上线。
十、方案落地最终收益
1. 零成本替代商业安全体系:替代商业威胁情报、SOC、自动封禁平台、入侵检测设备,节省数万安全预算;
2. 无人值守自动化防护:90%的攻击研判、处置、日志统计全自动完成,解决中小企业无专职安全人员痛点;
3. 极低误封、高精准防护:内外双源研判+分级处置+时间衰减,兼顾业务稳定与安全防护;
4. 完全满足等保合规:日志留存、攻击溯源、处置审计、风险研判全流程可追溯;
5. 自适应迭代升级:自建信誉库持续沉淀业务专属攻击特征,越用越精准,远超通用商业情报。
十一、全套落地源码|脚本 + DockerCompose + 定时任务(生产直接用)
本节为整套安全闭环最终可运行资产,包含:统一编排 docker-compose、Graylog 拉取可疑IP脚本、信誉系统对接钩子、设备联动封禁/解封脚本、hfish-push 完整服务、系统定时任务。全部兼容前文架构,无需二次修改。
11.1 整体目录结构(严格按此创建)
/opt/auto-sec-stack
├── docker-compose.yml
├── .env
├── maltrail/
│ └── maltrail.conf
├── secautoban/
│ ├── config.ini
│ ├── whitelist.conf
│ └── hook/
│ ├── maltrail_check_hook.py
│ ├── device_ban_hook.py
│ └── device_unban_hook.py
├── hfish/
│ └── hfish_push.py
├── scripts/
│ └── graylog_fetch_suspicious_ip.py
└── logs/
11.2 .env 全局环境变量
# Webhook 密钥(务必改为随机强密码)
HFISH_WEBHOOK_TOKEN=SecStack_2026_Random_Strong_Pwd
# 钉钉告警(无则留空)
DINGDING_WEBHOOK=
DINGDING_SECRET=
# 时区
TZ=Asia/Shanghai
11.3 maltrail/maltrail.conf
[server]
host = 127.0.0.1
port = 8337
[update]
interval = 3600
[sensor]
enabled = false
11.4 secautoban/whitelist.conf(全局唯一白名单)
# 内网网段
127.0.0.1
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
# 运维出口公网IP(自行补充)
# 业务、CDN、合作IP
11.5 secautoban/config.ini(开启执行钩子、适配信誉系统)
[global]
log_path = /var/log/SecAutoBan
ipset_name = SecAutoBan
ban_time = 28800
max_ban_count = 8192
[ssh]
enable = true
log_file = /var/log/auth.log
trigger_count = 5
[nginx]
enable = true
log_file = /var/log/nginx/access.log
trigger_count = 10
[hook]
enable = true
# 信誉复核钩子(替代原单纯 Maltrail 判断)
ban_hook_script = /etc/SecAutoBan/hook/maltrail_check_hook.py
# 执行多设备下发钩子
exec_ban_hook = /etc/SecAutoBan/hook/device_ban_hook.py
exec_unban_hook = /etc/SecAutoBan/hook/device_unban_hook.py
[alert]
enable = true
alert_type = dingtalk
dingtalk_webhook = ${DINGDING_WEBHOOK}
dingtalk_secret = ${DINGDING_SECRET}
[external_blacklist]
enable = false
11.6 secautoban/hook/maltrail_check_hook.py(【信誉系统复核钩子】核心改造)
不再只依赖 Maltrail,优先调用自建IP信誉服务,实现可信/可疑/高危分级,控制是否封禁。
#!/usr/bin/env python3
import sys
import requests
import logging
# 自建IP信誉服务接口
REPUTE_API = "http://reputation:9090/ip_score"
TIMEOUT = 5
logging.basicConfig(filename="/var/log/secautoban_repute.log", level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s")
def main():
if len(sys.argv) < 2:
sys.exit(1)
ip = sys.argv[1]
try:
resp = requests.get(REPUTE_API, params={"ip": ip}, timeout=TIMEOUT)
data = resp.json()
score = data.get("score", 0)
level = data.get("level", "trusted")
reason = data.get("reason", "")
logging.info(f"IP:{ip} Score:{score} Level:{level} Reason:{reason}")
# 规则:仅高风险、极高风险才允许封禁
if score > 50:
sys.exit(0) # 允许封禁
else:
sys.exit(1) # 不封禁(可信/可疑只告警)
except Exception as e:
logging.error(f"Repute API error: {str(e)}")
sys.exit(1)
if __name__ == "__main__":
main()
11.7 secautoban/hook/device_ban_hook.py(硬件防火墙/路由器 ACL 封禁下发)
#!/usr/bin/env python3
import sys
import paramiko
import logging
logging.basicConfig(filename="/var/log/device_ban.log", level=logging.INFO)
BAN_IP = sys.argv[1]
# 根据自己设备修改:H3C/华为/思科 命令适配
DEV_LIST = [
{
"ip": "192.168.1.1",
"user": "admin",
"pwd": "your-pass",
"cmd": "acl number 3999\n rule deny ip source {ip} 0\n save"
}
]
def ban(ip):
for d in DEV_LIST:
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(d["ip"], username=d["user"], password=d["pwd"], timeout=8)
for line in d["cmd"].format(ip=ip).splitlines():
ssh.exec_command(line.strip())
ssh.close()
logging.info(f"SUCCESS ban {ip} on {d['ip']}")
except Exception as e:
logging.error(f"FAIL ban {ip} {d['ip']}: {str(e)}")
if __name__ == "__main__":
ban(BAN_IP)
11.8 secautoban/hook/device_unban_hook.py(到期自动解封硬件ACL)
#!/usr/bin/env python3
import sys
import paramiko
import logging
logging.basicConfig(filename="/var/log/device_unban.log", level=logging.INFO)
BAN_IP = sys.argv[1]
DEV_LIST = [
{
"ip": "192.168.1.1",
"user": "admin",
"pwd": "your-pass",
"cmd": "acl number 3999\n undo rule deny ip source {ip} 0\n save"
}
]
def unban(ip):
for d in DEV_LIST:
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(d["ip"], username=d["user"], password=d["pwd"], timeout=8)
for line in d["cmd"].format(ip=ip).splitlines():
ssh.exec_command(line.strip())
ssh.close()
logging.info(f"SUCCESS unban {ip} on {d['ip']}")
except Exception as e:
logging.error(f"FAIL unban {ip} {d['ip']}: {str(e)}")
if __name__ == "__main__":
unban(BAN_IP)
11.9 hfish/hfish_push.py(统一接收 HFish + Graylog 双源事件)
#!/usr/bin/env python3
from flask import Flask, request, jsonify
import subprocess
import logging
import os
app = Flask(__name__)
logging.basicConfig(filename="/var/log/hfish_push.log", level=logging.INFO)
API_TOKEN = os.getenv("HFISH_WEBHOOK_TOKEN", "")
SECAUTOBAN_BIN = "/usr/bin/SecAutoBan"
def submit_ip(ip, source, attack_type):
logging.info(f"[PUSH] IP:{ip} SRC:{source} TYPE:{attack_type}")
try:
subprocess.run([SECAUTOBAN_BIN, "ban", "--ip", ip], timeout=10)
except Exception as e:
logging.error(f"[PUSH ERR] {ip} {str(e)}")
# 蜜罐事件
@app.route("/hfish/event", methods=["POST"])
def hfish_event():
if request.headers.get("X-Token", "") != API_TOKEN:
return jsonify({"code":403}),403
d = request.get_json() or {}
ip = d.get("src_ip")
if ip:
submit_ip(ip, "hfish", "honeypot_attack")
return jsonify({"ok":1})
# Graylog 聚合可疑IP事件
@app.route("/graylog/event", methods=["POST"])
def graylog_event():
if request.headers.get("X-Token", "") != API_TOKEN:
return jsonify({"code":403}),403
d = request.get_json() or {}
ip = d.get("src_ip")
if ip:
submit_ip(ip, "graylog", "multi_device_attack")
return jsonify({"ok":1})
if __name__ == "__main__":
app.run("0.0.0.0", 8889, False)
11.10 scripts/graylog_fetch_suspicious_ip.py(5分钟聚合筛选可疑IP)
#!/usr/bin/env python3
import requests, json, logging, os
# ========== 配置区 ==========
GRAYLOG_API = "http://127.0.0.1:9000/api"
GRAYLOG_USER = "admin"
GRAYLOG_PASS = "你的密码"
STREAM_ID = "你的安全数据流ID"
TIME_WINDOW = 300
MIN_COUNT = 3
PUSH_URL = "http://hfish-push:8889/graylog/event"
TOKEN = os.getenv("HFISH_WEBHOOK_TOKEN","SecStack_2026_Random_Strong_Pwd")
WHITE_LIST_PATH = "/opt/auto-sec-stack/secautoban/whitelist.conf"
# ============================
logging.basicConfig(filename="/var/log/graylog_fetch.log", level=logging.INFO)
def load_white():
wl = set()
try:
with open(WHITE_LIST_PATH,"r") as f:
for line in f:
line=line.strip()
if line and not line.startswith("#"):
wl.add(line)
except:
pass
return wl
def fetch():
headers = {"Content-Type":"application/json"}
query = '''
(attack_type:SQLi OR attack_type:XSS OR attack_type:PathTraversal OR attack_type:RCE)
OR (signature_id:>0)
OR action:(drop reject)
OR message:("authentication failed" "login fail")
'''
payload = {
"query": query,
"streams": [STREAM_ID],
"range": {"type":"relative","from":TIME_WINDOW*1000},
"aggregation":{"group_by":[{"type":"field","field":"src_ip"}],"metrics":[{"type":"count"}]}
}
res = requests.post(f"{GRAYLOG_API}/search/aggregation",
auth=(GRAYLOG_USER,GRAYLOG_PASS),headers=headers,json=payload,timeout=20)
buckets = res.json().get("aggregation",{}).get("buckets",[])
wl = load_white()
out = []
for b in buckets:
ip = b.get("key")
cnt = b.get("count",0)
if ip and ip not in wl and cnt >= MIN_COUNT:
out.append(ip)
return out
def push(ip):
headers = {"X-Token":TOKEN,"Content-Type":"application/json"}
body = {"src_ip":ip,"attack_type":"multi_device_scan_brute"}
requests.post(PUSH_URL,json=body,headers=headers,timeout=10)
def main():
ips = fetch()
logging.info(f"Fetch suspicious ips: {ips}")
for ip in ips:
push(ip)
if __name__=="__main__":
main()
11.11 docker-compose.yml(整套三组件+调度一键编排)
version: '3.8'
services:
maltrail:
image: python:3.11-slim
container_name: maltrail
restart: always
volumes:
- ./maltrail/maltrail.conf:/opt/maltrail/conf/maltrail.conf
- maltrail_data:/opt/maltrail
- ./logs:/var/log
working_dir: /opt/maltrail
command: >
bash -c "git clone https://github.com/stamparm/maltrail.git /opt/maltrail 2>/dev/null || true &&
python3 install.py &&
python3 maltrail-server.py"
networks:
- sec_net
expose:
- "8337"
environment:
TZ: ${TZ}
secautoban:
image: python:3.11-slim
container_name: secautoban
restart: always
privileged: true
cap_add:
- NET_ADMIN
- NET_RAW
volumes:
- ./secautoban/config.ini:/etc/SecAutoBan/config.ini
- ./secautoban/whitelist.conf:/etc/SecAutoBan/whitelist.conf
- ./secautoban/hook:/etc/SecAutoBan/hook
- secautoban_data:/etc/SecAutoBan
- ./logs:/var/log
- /var/log:/host/var/log:ro
working_dir: /opt/SecAutoBan
command: >
bash -c "git clone https://github.com/secauto/SecAutoBan.git /opt/SecAutoBan 2>/dev/null || true &&
python3 install.py &&
chmod +x /etc/SecAutoBan/hook/*.py &&
python3 main.py"
networks:
- sec_net
expose:
- "9000"
environment:
TZ: ${TZ}
DINGDING_WEBHOOK: ${DINGDING_WEBHOOK}
DINGDING_SECRET: ${DINGDING_SECRET}
hfish:
image: debian:12-slim
container_name: hfish
restart: always
volumes:
- hfish_data:/opt/hfish
- ./logs:/var/log
working_dir: /opt/hfish
command: >
bash -c "apt update && apt install -y wget unzip &&
wget -q https://github.com/hfish-security/HFish/releases/latest/download/hfish-linux-amd64.zip -O hfish.zip &&
unzip -o hfish.zip &&
./hfish-server"
networks:
- sec_net
ports:
- "2222:2222"
- "2121:2121"
- "2323:2323"
- "33060:33060"
- "8080:8080"
expose:
- "9001"
environment:
TZ: ${TZ}
hfish-push:
image: python:3.11-slim
container_name: hfish-push
restart: always
volumes:
- ./hfish/hfish_push.py:/opt/hfish_push.py
- ./logs:/var/log
working_dir: /opt
command: >
bash -c "pip3 install flask requests && python3 hfish_push.py"
networks:
- sec_net
expose:
- "8889"
environment:
TZ: ${TZ}
HFISH_WEBHOOK_TOKEN: ${HFISH_WEBHOOK_TOKEN}
networks:
sec_net:
driver: bridge
volumes:
maltrail_data:
secautoban_data:
hfish_data:
11.12 系统定时任务(crontab)
# 每5分钟执行一次Graylog可疑IP抓取
*/5 * * * * /usr/bin/python3 /opt/auto-sec-stack/scripts/graylog_fetch_suspicious_ip.py >> /var/log/graylog_cron.log 2>&1
11.13 Graylog Pipeline 归一规则(直接导入)
rule normalize_src_ip
when true then
set_field("src_ip", coalesce(
$message.src,
$message.source_ip,
$message.srcaddr,
$message."src-ip",
$message.SourceIP,
$message.src_ip
));
end
十二、上线最终检查清单(投产必做)
-
所有脚本已授权可执行:
chmod +x *.py -
修改设备登录密码、Graylog账号密码、Webhook密钥
-
白名单已填入内网、运维、CDN、业务IP
-
硬件ACL规则为空、避免冲突
-
先关闭自动封禁执行,观察3天日志无误判再开启
-
确认所有组件日志正常落盘、Graylog正常收录