SecMindX 使用说明

本地 AI 增强型日志安全智能研判平台 · 安装部署 / 日常运维 / 进阶优化 · 返回官网首页

SecMindX 使用说明(完整版)

SecMindX —— 本地 AI 增强型日志安全智能研判平台。

主程序负责定时批量分析日志(硬规则 + 本地大模型双引擎),Web 提供仪表盘、告警中心、Agent 管理、配置管理等功能。

本文按使用流程从安装到进阶逐项说明,供部署与日常运维参考。

一句话理解这个系统:

Agent 负责"收日志",主程序负责"读日志、找问题",Web 负责"给你看结果、让你动手处置"。

★ 快速上手(写给第一次用的人)

第 1 步:装好平台(见第 1 节),登录 Web,默认账号 admin / 密码 admin123,登录后马上改密码

第 2 步:把 ES 和大模型的地址填进【配置管理 → 基础配置】(见 2.2),保存

第 3 步:给要监控的电脑/服务器装上 Agent(见第 3 节),装完 Agent 自动把日志送上来

第 4 步:打开【安全仪表盘】,看到红色高危告警就点进去看详情

第 5 步:确认是攻击 → 封掉高危公开情报源 IP(见第 13 节);确认是误报 → 点"永久标记安全",让它以后别再吵

第 6 步:日志多了看不过来 → 开高速模式、做降噪(见第 4、5 节),越用越准

0、前置条件

1、下载安装

1.0 安装 Elasticsearch(未自备 ES 时;使用清华镜像,解决官方源下载慢):

wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/elasticsearch-keyring.gpg] https://mirrors.tuna.tsinghua.edu.cn/elasticstack/8.x/apt/ stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list
sudo apt update
sudo apt install elasticsearch
# 创建备份仓库目录
mkdir -p /opt/secmindx/backup/es-repo
chown -R elasticsearch:elasticsearch /opt/secmindx/backup/es-repo
# 配置备份仓库路径(path.repo)
grep -q 'path.repo:' /etc/elasticsearch/elasticsearch.yml || echo 'path.repo: ["/opt/secmindx/backup/es-repo"]' | sudo tee -a /etc/elasticsearch/elasticsearch.yml
# 重启使配置生效
sudo systemctl daemon-reload
sudo systemctl enable --now elasticsearch.service
#sudo systemctl status elasticsearch.service
# 修改 elastic 用户密码(平台连接用)
sudo /usr/share/elasticsearch/bin/elasticsearch-reset-password -u elastic -i

1.1 下载分卷并合并解压(Linux 服务器,root 执行):

wget https://www.secmindx.com/secmindx_aa && \
wget https://www.secmindx.com/secmindx_ab && \
cat secmindx_aa secmindx_ab > secmindx.tar.gz && \
tar xzf secmindx.tar.gz && \
cd secmindx && \
chmod +x install.sh && \
sudo ./install.sh

1.2 验证Web登录:

浏览器访问 http://<平台IP>:8888,能打开登录页即安装成功。

默认账号:admin 密码:admin123 建议立即改密码

1.3 服务说明:

常用命令:

systemctl status secmindx-web secmindx-analyze.timer
journalctl -u secmindx-analyze -n 50
systemctl start secmindx-analyze   # 手动立即跑一轮
systemctl restart secmindx-web

2、登录与初始设置

2.1 登录:默认账号 admin,默认密码 admin123(首次登录后请立即改密)。

改密位置:Web → 用户管理(Web 管理)→ 修改密码。

2.2 基础配置

2.3 设置告警:

2.4 授权文件:

2.5 其他保持默认即可。

3、Agent 批量安装

3.1 Web →【Agent 管理】 → 打开“自动注册”(批量安装用,装完关闭)。

3.2 Linux 目标机安装示例:

mkdir -p /opt/smxagent
curl -o /opt/smxagent/SMXAgent-linux-amd64 \
http://<平台IP>:8888/agent-download/SMXAgent-linux-amd64
chmod +x /opt/smxagent/SMXAgent-linux-amd64
/opt/smxagent/SMXAgent-linux-amd64 -manager=http://<平台IP>:8888 --install

3.3 Windows 目标机安装示例:

管理员运行 PowerShell 代码:

mkdir -p "C:\Program Files\smxagent\"
Invoke-WebRequest -Uri "http://<平台IP>:8888/agent-download/SMXAgent-windows-amd64.exe" -OutFile "C:\Program Files\smxagent\\SMXAgent.exe"
cmd
cd "C:\Program Files\smxagent\"
SMXAgent.exe -manager=http://<平台IP>:8888 --install
sc start "SMXAgent"

3.4 安装完成后 Agent 自动心跳上报,出现在 Agent 列表;

关闭自动注册后,新 Agent 需手动安装(帮助页有可复制命令)。

3.5 已支持平台:Windows(自动开启关键审计子类目)、主流 Linux(自动部署 auditd 规则)、交换机 / 防火墙 / vCenter 等网络设备。

4、高速模式(进阶)-大幅提升处理速度

Web →【配置管理】→ AI 节流(Pro) → 高速模式:默认打开

5、降噪

5.1 内置降噪:

5.2 永久标记安全(最重要的降噪手段):

告警中心 → 某条日志 → “永久标记安全”:

5.3 特征文件:

可在 Web 配置管理 → AI 节流中分页管理、删除。

6、过滤 Windows / Linux

7、Agent 进阶管理

8、日志分析处置

8.1 告警中心:

8.2 处置动作:

8.3 监控组(权限监控):

privilege_monitor.watch_local_groups(如 Administrators)与 watch_ad_groups(如 Domain Admins)组变更即告警。

8.4 关联分析(横向移动等):

内置攻击关联特征:RDP 多主机跳转、远程任务 4698、异常时间登录、服务安装 7045、暴力破解阈值(次数 + 时间窗口,可自定义)等。

9、交换机 / 防火墙/vcneter处理

10、特征提取

10.1 主程序对每条日志生成特征指纹:

10.2 Web → 智脑日志:

10.3 重新分析:

从 ES 清除“已分析标记”后,主程序会对全部日志重新分析(用于特征规则更新后的全量重算)

11、ES 优化(该项已自动处理)

11.1 查询超 10000 条限制:

WEB → 智脑日志 → 设置 ES 查询窗口大小

11.2 备份与还原(Web → 备份还原):

12、大模型优化

13、高危公开情报源 IP 封禁(重点)

这是什么:

凡是命中情报库的 IP 视为高危攻击源,可自动下发封禁,从源头阻断攻击。

不是你们公司内部的正常 IP。

和"风险 IP"的关系:

怎么封(两种方式):

方式一:自动封禁(推荐,长期有效)

Web → 配置管理 → 自动封禁卡片:

方式二:手动封禁(临时应急)

告警中心 / Agent 管理 → 对指定的来源 IP 一键封禁。

封禁前必须确认(防误伤):

提前加进白名单(见第 7 节:rdp_safe_ips / safe_ips 等),

白名单里的 IP 永不封禁

封禁后:

"永久标记安全",避免同一个 IP 反复误封

常见问题

Q1 忘记密码?

Q2 交换机防火墙如何接入?

sudo tee /etc/logstash/conf.d/beats.conf >/dev/null <<'EOF'
# AI-SIEM pipeline: beats + UDP syslog
input {
  beats { port => 5044 }
  udp {
    port => 5140
    host => "0.0.0.0"
    codec => plain { charset => "ASCII-8BIT" }
  }
}
filter {
  # GBK转UTF8,放在最前面,先处理原始消息编码
  ruby {
    code => '
        raw_msg = event.get("message")
        if raw_msg
            begin
                fixed_msg = raw_msg.force_encoding("GBK").encode("UTF-8", :invalid => :replace, :undef => :replace)
                event.set("message", fixed_msg)
            rescue
            end
        end
    '
  }

  #1. copy fields.log_type -> log_type
  if ![log_type] and [fields][log_type] {
    mutate { add_field => { "log_type" => "%{[fields][log_type]}" } }
  }
  #2. Ruby: use host[ip] to auto set log_type (log_type为空时,按来源IP自动分类)
  if ![log_type] and [host][ip] {
    ruby {
      code => '
        require "ipaddr" unless defined?(IPAddr)
        $ai_rules ||= {
          "switch"   => ["10.100.100.11", "10.100.100.12"],
          "firewall" => ["10.100.100.1", "10.100.100.4"],
          "vcenter"  => ["172.16.1.107", "172.16.1.49"]
        }
        ip = event.get("[host][ip]").to_s rescue ""
        begin
          o = IPAddr.new(ip)
          $ai_rules.each do |t, exprs|
            hit = exprs.any? do |e|
              if e.include?("/")
                IPAddr.new(e).include?(o)
              elsif e.include?("-")
                a,b = e.split("-",2)
                v = o.to_i
                v >= IPAddr.new(a.strip).to_i && v <= IPAddr.new(b.strip).to_i
              else
                e.strip == ip
              end
            end
            if hit
              event.set("log_type", t)
              break
            end
          end
        rescue => e
        end
      '
    }
  }
  #3 Windows Event
  if [log_type] == "windows_event" {
    if [winlog][event_id] not in [4624,4625,4778,4779,4672,1074,6005,6006] and [winlog][event_id] in [0,2,3,7,15,32,36,102,103,105,151,216,256,257,258,300,301,302,326,400,513,700,701,754,900,902,903,1000,1001,1003,1008,1011,1014,1015,1033,1035,1042,1066,1500,1501,1502,1503,1511,1704,1803,2001,2003,2005,2006,2048,2049,2319,2887,3006,4101,4201,4768,4771,5805,5857,6003,6394,7035,7036,7040,7041,8033,8193,8194,8197,8224,8230,9027,12289,16384,16394,16998,20021,20022,23022] {
        drop {}
    }
    mutate {
      replace => { "log_type" => "windows_event" }
      add_field => { "[@metadata][idx]" => "windows_event-log-%{+YYYY.MM.dd}" }
    }
    if [winlog][event_id] {
      mutate { add_field => { "event_id" => "%{[winlog][event_id]}" } }
    }
    if [winlog][event_id] == 4625 { mutate { add_tag => ["login_failed", "security_alert"] } }
    if [winlog][event_id] == 4624 { mutate { add_tag => ["login_success"] } }
    if [winlog][event_id] == 4720 { mutate { add_tag => ["account_created", "security_alert"] } }
    if [winlog][event_id] == 4728 { mutate { add_tag => ["domain_group_change", "security_alert"] } }
    if [winlog][event_id] == 4732 { mutate { add_tag => ["local_group_change", "security_alert"] } }
    if [winlog][event_id] == 4776 { mutate { add_tag => ["credential_validate"] } }
    if [winlog][event_id] == 4768 { mutate { add_tag => ["kerberos_tgt"] } }
    if [winlog][event_id] == 4771 { mutate { add_tag => ["kerberos_preauth_failed"] } }
    if [winlog][event_id] == 5722 { mutate { add_tag => ["secure_channel_failed", "security_alert"] } }
    if [winlog][event_id] == 7036 { mutate { add_tag => ["service_state_change"] } }
  }
  #4 Linux
  if [log_type] == "linux" {
    mutate {
      replace => { "log_type" => "linux" }
      add_field => { "[@metadata][idx]" => "linux-log-%{+YYYY.MM.dd}" }
    }
  }
  #5 Nginx
  if [log_type] == "nginx" {
    mutate {
      replace => { "log_type" => "nginx" }
      add_field => { "[@metadata][idx]" => "nginx-log-%{+YYYY.MM.dd}" }
    }
  }
  #6 MySQL
  if [log_type] == "mysql" {
    mutate {
      replace => { "log_type" => "mysql" }
      add_field => { "[@metadata][idx]" => "mysql-log-%{+YYYY.MM.dd}" }
    }
  }
  #7 Switch
  if [log_type] == "switch" {
    mutate {
      replace => { "log_type" => "switch" }
      add_field => { "[@metadata][idx]" => "switch-log-%{+YYYY.MM.dd}" }
    }
  }
  #8 Firewall Fortinet
  if [log_type] == "firewall" {
    mutate {
      replace => { "log_type" => "firewall" }
      add_field => { "[@metadata][idx]" => "firewall-log-%{+YYYY.MM.dd}" }
    }
    grok {
      match => { "message" => "<%{POSINT:syslog_pri}>%{GREEDYDATA:msg_raw}" }
    }
    kv {
      source => "msg_raw"
      field_split => " "
      value_split => "="
      trim_key => "\""
      trim_value => "\""
    }
    if [srcip] {
      mutate { add_field => { "src_zone" => "internet" } }
      cidr {
        address => [ "%{srcip}" ]
        network => [
          "10.10.10.0/24",
          "10.20.20.0/24",
          "10.0.0.0/8",
          "172.16.0.0/12",
          "192.168.0.0/16"
        ]
        add_field => { "src_zone" => "internal" }
      }
    }
    if [dstip] {
      mutate { add_field => { "dst_zone" => "internet" } }
      cidr {
        address => [ "%{dstip}" ]
        network => [
          "10.10.10.0/24",
          "10.20.20.0/24",
          "10.0.0.0/8",
          "172.16.0.0/12",
          "192.168.0.0/16"
        ]
        add_field => { "dst_zone" => "internal" }
      }
    }
    mutate {
      convert => {
        "srcport" => "integer"
        "dstport" => "integer"
        "policyid" => "integer"
      }
    }
    if [date] and [time] {
      mutate {
        add_field => {
          "[@metadata][forti_time]" => "%{[date]} %{[time]}"
        }
      }
      date {
        match => [ "[@metadata][forti_time]", "yyyy-MM-dd HH:mm:ss" ]
        target => "@timestamp"
      }
    }
  }
  #9 vCenter
  if [log_type] == "vcenter" {
    mutate {
      replace => { "log_type" => "vcenter" }
      add_field => { "[@metadata][idx]" => "vcenter-log-%{+YYYY.MM.dd}" }
    }
  }
  #10 unknown
  if ![@metadata][idx] {
    mutate {
      replace => { "log_type" => "unknown" }
      add_field => { "[@metadata][idx]" => "unknown-log-%{+YYYY.MM.dd}" }
      add_tag => ["unknown_log_type"]
    }
  }
  #11 device_ip / device_hostname
  if [log_type] in ["switch", "firewall", "vcenter", "unknown"] and [host][ip] {
    mutate {
      add_field => {
        "device_ip"       => "%{[host][ip]}"
        "device_hostname" => "%{[host][name]}"
      }
    }
  }
  #12 node_id
  if [log_type] in ["switch", "firewall", "vcenter", "unknown"] {
    if [device_hostname] and [device_hostname] != "-" {
      mutate { add_field => { "node_id" => "%{[device_hostname]}" } }
    } else if [device_ip] {
      mutate { add_field => { "node_id" => "%{[device_ip]}" } }
    } else {
      mutate { add_field => { "node_id" => "unknown-node" } }
    }
  } else if [log_type] in ["windows_event", "linux", "nginx", "mysql"] {
    if [host][id] {
      mutate { add_field => { "node_id" => "%{[host][id]}" } }
    } else if [agent][id] {
      mutate { add_field => { "node_id" => "%{[agent][id]}" } }
    } else if [host][name] {
      mutate { add_field => { "node_id" => "%{[host][name]}" } }
    }
  }
}
output {
  elasticsearch {
    hosts => ["https://127.0.0.1:9200"]
    user => "elastic"
    password => "password"
    ssl_enabled => true
    ssl_verification_mode => "none"
    index => "%{[@metadata][idx]}"
  }
  # stdout { codec => rubydebug }
}
EOF

名词解释(通俗版)

Agent装在每台被监控机器上的"小哨兵",负责采集日志上报平台
ES(Elasticsearch)存放和检索日志的数据库
大模型(LLM)AI 大脑,读日志并给出风险结论
硬规则内置的固定安全规则,不依赖 AI 也能识别常见攻击
特征(ai_sig)日志的"指纹",同类事件指纹相同,用于批量归类
降噪把无害日志过滤掉,只留真正需要看的
告警发现风险后通过钉钉/企微等推送给你的通知
封禁阻断某个 IP 访问你的机器
白名单永不封禁、永不告警的合法 IP / 账号清单
处置 / 已处理人工看完确认处理过
分析时间AI 看完这条日志的时刻;日志时间 = 日志本身产生的时刻
AI 节流相同特征只分析一次,后续直接复用结果,省算力
已分析标记日志是否被 AI 看过的记号;清除后主程序会重新分析全部