fix(security,data): P0 — 命令注入/停止毁数据/采集路径绕过/DB 清库/前端竞态
后端(实机 10.10.10.161 验证 host-binary 全链路 + 注入双路径 400): - Loki 版本号严格 ^\d+.\d+.\d+$ 校验并接入 Update 路径:version 拼进 SSH shell 命令, 旧校验放行 3.0.0'; id; echo ' 等注入载荷(root RCE) - Stop/Restart 清理清单移除 PVC:停止部署曾连带删除 loki-data PVC,filesystem 存储日志被永久清空(测试锁死红线) - 采集敏感路径防护覆盖默认 LogPath 分支与 collector Create(/etc/shadow 不再可绕过入库) - 数据文件损坏时原文件改名 .corrupt-* 留档再空库启动,不再被静默覆盖(loadFromDisk 抽出可测) 前端: - 审计页用户名过滤 300ms 防抖 + AbortController/版本号(逐键竞态数据错乱) - 向导自动部署改用创建返回实例(或 id 最大且 pending),不再盲取 list[0] 对错误实例发起真实部署 - 路由级 ErrorBoundary:单页崩溃/chunk 404 只影响当前页
This commit is contained in:
1 parent
5e59347c40
commit
2975452c21
17 files changed
+271
-68
No files matched your search
+63
-43
@@ -539,50 +539,25 @@ func (d *JsonDB) migrateSchema() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func InitDB() error {
|
||||
cfg := config.GetConfig()
|
||||
_ = os.MkdirAll(cfg.DataDir, 0700)
|
||||
|
||||
d := &JsonDB{
|
||||
dataFile: cfg.DatabasePath,
|
||||
Users: []models.User{},
|
||||
Clusters: []models.Cluster{},
|
||||
Hosts: []models.Host{},
|
||||
Storages: []models.StorageConfig{},
|
||||
Deployments: []models.LokiDeployment{},
|
||||
Collectors: []models.Collector{},
|
||||
Logs: []models.DeploymentLog{},
|
||||
Settings: []models.SystemSetting{},
|
||||
LogSources: []models.LogSource{},
|
||||
AlertRules: []models.AlertRule{},
|
||||
AlertDeliveries: []models.AlertDelivery{},
|
||||
NotifyChannels: []models.NotifyChannel{},
|
||||
LogPolicyGroups: []models.LogPolicyGroup{},
|
||||
AuditLogs: []models.AuditLog{},
|
||||
DailyVolumeSnapshots: []models.DailyVolumeSnapshot{},
|
||||
SchemaVersion: CurrentSchemaVersion,
|
||||
sequences: map[string]uint{
|
||||
"users": 0,
|
||||
"clusters": 0,
|
||||
"hosts": 0,
|
||||
"storages": 0,
|
||||
"deployments": 0,
|
||||
"collectors": 0,
|
||||
"logs": 0,
|
||||
"logPolicyGroups": 0,
|
||||
"logSources": 0,
|
||||
"notifyChannels": 0,
|
||||
"alertRules": 0,
|
||||
"alertDeliveries": 0,
|
||||
"auditLogs": 0,
|
||||
"dailyVolumeSnapshots": 0,
|
||||
},
|
||||
saveChan: make(chan saveRequest, 10),
|
||||
stopChan: make(chan struct{}),
|
||||
}
|
||||
|
||||
// loadFromDisk 读取并解析数据文件,重建 ID 序列;文件损坏时备份留档绝不覆盖。
|
||||
func (d *JsonDB) loadFromDisk() {
|
||||
if data, err := os.ReadFile(d.dataFile); err == nil {
|
||||
if err := json.Unmarshal(data, d); err == nil {
|
||||
if err := json.Unmarshal(data, d); err != nil {
|
||||
// 损坏的数据文件可能已被部分填充,绝不能让随后的空库落盘覆盖原文件:
|
||||
// 先把原文件改名留档,再清回空库继续启动(seed 会重建 admin)。
|
||||
// 改名失败(权限/只读)时宁可退出,也不销毁可能可恢复的数据。
|
||||
backup := fmt.Sprintf("%s.corrupt-%s", d.dataFile, time.Now().Format("20060102-150405"))
|
||||
if renameErr := os.Rename(d.dataFile, backup); renameErr != nil {
|
||||
log.Fatalf("[db] 数据文件 %s 解析失败(%v)且无法备份(%v),为避免覆盖可恢复数据进程退出,请人工处理", d.dataFile, err, renameErr)
|
||||
}
|
||||
log.Printf("[ERROR] [db] 数据文件 %s 解析失败(%v),原文件已备份为 %s,以空库继续启动", d.dataFile, err, backup)
|
||||
d.Users, d.Clusters, d.Hosts, d.Storages = nil, nil, nil, nil
|
||||
d.Deployments, d.Collectors, d.Logs, d.Settings = nil, nil, nil, nil
|
||||
d.LogSources, d.AlertRules, d.AlertDeliveries = nil, nil, nil
|
||||
d.NotifyChannels, d.LogPolicyGroups, d.AuditLogs = nil, nil, nil
|
||||
d.DailyVolumeSnapshots = nil
|
||||
d.SchemaVersion = 0
|
||||
} else {
|
||||
d.sequences = map[string]uint{}
|
||||
for _, u := range d.Users {
|
||||
if u.ID > d.sequences["users"] {
|
||||
@@ -661,6 +636,51 @@ func InitDB() error {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func InitDB() error {
|
||||
cfg := config.GetConfig()
|
||||
_ = os.MkdirAll(cfg.DataDir, 0700)
|
||||
|
||||
d := &JsonDB{
|
||||
dataFile: cfg.DatabasePath,
|
||||
Users: []models.User{},
|
||||
Clusters: []models.Cluster{},
|
||||
Hosts: []models.Host{},
|
||||
Storages: []models.StorageConfig{},
|
||||
Deployments: []models.LokiDeployment{},
|
||||
Collectors: []models.Collector{},
|
||||
Logs: []models.DeploymentLog{},
|
||||
Settings: []models.SystemSetting{},
|
||||
LogSources: []models.LogSource{},
|
||||
AlertRules: []models.AlertRule{},
|
||||
AlertDeliveries: []models.AlertDelivery{},
|
||||
NotifyChannels: []models.NotifyChannel{},
|
||||
LogPolicyGroups: []models.LogPolicyGroup{},
|
||||
AuditLogs: []models.AuditLog{},
|
||||
DailyVolumeSnapshots: []models.DailyVolumeSnapshot{},
|
||||
SchemaVersion: CurrentSchemaVersion,
|
||||
sequences: map[string]uint{
|
||||
"users": 0,
|
||||
"clusters": 0,
|
||||
"hosts": 0,
|
||||
"storages": 0,
|
||||
"deployments": 0,
|
||||
"collectors": 0,
|
||||
"logs": 0,
|
||||
"logPolicyGroups": 0,
|
||||
"logSources": 0,
|
||||
"notifyChannels": 0,
|
||||
"alertRules": 0,
|
||||
"alertDeliveries": 0,
|
||||
"auditLogs": 0,
|
||||
"dailyVolumeSnapshots": 0,
|
||||
},
|
||||
saveChan: make(chan saveRequest, 10),
|
||||
stopChan: make(chan struct{}),
|
||||
}
|
||||
|
||||
d.loadFromDisk()
|
||||
migratedLogAccess := migrateLogAccessToGroups(d)
|
||||
// 结构版本迁移(审计表、采集项等新字段的兼容处理)
|
||||
migratedSchema := d.migrateSchema()
|
||||
|
||||
Reference in new issue
Block a user