优化目录保护
This commit is contained in:
1 parent
f43a856add
commit
3cf4d7e5a9
9 files changed
+226
-184
No files matched your search
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/dushixiang/pika/internal/utils"
|
||||
"github.com/go-orz/orz"
|
||||
"github.com/labstack/echo/v4"
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
// Get 获取探针详情(公开接口,已登录返回全部,未登录返回公开可见)
|
||||
@@ -22,6 +23,10 @@ func (h *AgentHandler) Get(c echo.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 隐藏敏感配置
|
||||
agent.SSHLoginConfig = datatypes.JSONType[models.SSHLoginConfigData]{}
|
||||
agent.TamperProtectConfig = datatypes.JSONType[models.TamperProtectConfigData]{}
|
||||
|
||||
// 未登录时隐藏敏感信息
|
||||
if !isAuthenticated {
|
||||
agent.IP = ""
|
||||
|
||||
@@ -86,10 +86,10 @@ func (h *AgentHandler) handleWebSocketMessage(ctx context.Context, agentID strin
|
||||
return h.handleSSHLoginEventMessage(ctx, agentID, data)
|
||||
|
||||
case protocol.MessageTypeSSHLoginConfigResult:
|
||||
return h.handleSSHLoginConfigResultMessage(agentID, data)
|
||||
return h.handleSSHLoginConfigResultMessage(ctx, agentID, data)
|
||||
|
||||
case protocol.MessageTypeTamperProtect:
|
||||
return h.handleTamperProtectMessage(agentID, data)
|
||||
return h.handleTamperProtectMessage(ctx, agentID, data)
|
||||
|
||||
default:
|
||||
h.logger.Warn("unknown message type", zap.String("type", messageType))
|
||||
@@ -192,34 +192,22 @@ func (h *AgentHandler) handleSSHLoginEventMessage(ctx context.Context, agentID s
|
||||
return h.sshLoginService.HandleEvent(ctx, agentID, eventData)
|
||||
}
|
||||
|
||||
func (h *AgentHandler) handleSSHLoginConfigResultMessage(agentID string, data json.RawMessage) error {
|
||||
func (h *AgentHandler) handleSSHLoginConfigResultMessage(ctx context.Context, agentID string, data json.RawMessage) error {
|
||||
var resultData protocol.SSHLoginConfigResult
|
||||
if err := json.Unmarshal(data, &resultData); err != nil {
|
||||
h.logger.Error("failed to unmarshal ssh login config result", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return h.sshLoginService.HandleConfigResult(agentID, resultData)
|
||||
return h.sshLoginService.HandleConfigResult(ctx, agentID, resultData)
|
||||
}
|
||||
|
||||
func (h *AgentHandler) handleTamperProtectMessage(agentID string, data json.RawMessage) error {
|
||||
func (h *AgentHandler) handleTamperProtectMessage(ctx context.Context, agentID string, data json.RawMessage) error {
|
||||
var protectResp protocol.TamperProtectResponse
|
||||
if err := json.Unmarshal(data, &protectResp); err != nil {
|
||||
h.logger.Error("failed to unmarshal tamper protect response", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
if protectResp.Success {
|
||||
h.logger.Info("tamper protect config applied successfully",
|
||||
zap.String("agentID", agentID),
|
||||
zap.String("message", protectResp.Message),
|
||||
zap.Int("current_paths", len(protectResp.Paths)),
|
||||
zap.Int("added", len(protectResp.Added)),
|
||||
zap.Int("removed", len(protectResp.Removed)))
|
||||
} else {
|
||||
h.logger.Error("tamper protect config apply failed",
|
||||
zap.String("agentID", agentID),
|
||||
zap.String("message", protectResp.Message))
|
||||
}
|
||||
return nil
|
||||
return h.tamperService.HandleConfigResult(ctx, agentID, protectResp)
|
||||
}
|
||||
|
||||
// sendRegisterSuccess 发送注册成功响应
|
||||
@@ -249,24 +237,16 @@ func (h *AgentHandler) sendRegisterError(conn *websocket.Conn, errMsg string) er
|
||||
|
||||
// sendTamperConfig 发送防篡改配置(探针初始化时发送完整配置作为新增)
|
||||
func (h *AgentHandler) sendTamperConfig(conn *websocket.Conn, agentID string) error {
|
||||
// 获取探针的防篡改配置
|
||||
config, err := h.tamperService.GetConfigByAgentID(agentID)
|
||||
// 使用 TamperService 构建初始配置(复用逻辑,会自动判断 enabled 状态)
|
||||
added, removed, err := h.tamperService.BuildInitialConfig(context.Background(), agentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 构建配置数据 - 将完整配置作为新增发送(探针刚连接,所有路径都是新增)
|
||||
var paths []string
|
||||
if config != nil && len(config.Paths) > 0 {
|
||||
paths = config.Paths
|
||||
} else {
|
||||
paths = []string{} // 空列表
|
||||
}
|
||||
|
||||
// 使用增量配置格式,将所有路径作为新增
|
||||
// 使用增量配置格式
|
||||
configData := protocol.TamperProtectConfig{
|
||||
Added: paths,
|
||||
Removed: []string{}, // 初始化时没有需要移除的
|
||||
Added: added,
|
||||
Removed: removed,
|
||||
}
|
||||
|
||||
msgData, err := json.Marshal(protocol.OutboundMessage{
|
||||
|
||||
@@ -34,7 +34,7 @@ func (h *SSHLoginHandler) GetConfig(c echo.Context) error {
|
||||
})
|
||||
}
|
||||
|
||||
config, err := h.service.GetConfig(agentID)
|
||||
config, err := h.service.GetConfig(c.Request().Context(), agentID)
|
||||
if err != nil {
|
||||
h.logger.Error("获取SSH登录监控配置失败", zap.Error(err))
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{
|
||||
@@ -42,13 +42,6 @@ func (h *SSHLoginHandler) GetConfig(c echo.Context) error {
|
||||
})
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
// 返回默认配置
|
||||
return c.JSON(http.StatusOK, map[string]interface{}{
|
||||
"enabled": false,
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, config)
|
||||
}
|
||||
|
||||
@@ -72,7 +65,7 @@ func (h *SSHLoginHandler) UpdateConfig(c echo.Context) error {
|
||||
})
|
||||
}
|
||||
|
||||
config, configSent, err := h.service.UpdateConfig(c.Request().Context(), agentID, req.Enabled)
|
||||
err := h.service.UpdateConfig(c.Request().Context(), agentID, req.Enabled)
|
||||
if err != nil {
|
||||
h.logger.Error("更新SSH登录监控配置失败", zap.Error(err))
|
||||
return c.JSON(http.StatusInternalServerError, map[string]string{
|
||||
@@ -80,19 +73,7 @@ func (h *SSHLoginHandler) UpdateConfig(c echo.Context) error {
|
||||
})
|
||||
}
|
||||
|
||||
// 根据下发状态返回不同的消息
|
||||
message := "配置已保存"
|
||||
if configSent {
|
||||
message = "配置已保存并成功下发到探针"
|
||||
} else {
|
||||
message = "配置已保存,将在探针下次连接时生效"
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, map[string]interface{}{
|
||||
"message": message,
|
||||
"config": config,
|
||||
"configSent": configSent, // 告知前端配置是否成功下发
|
||||
})
|
||||
return c.JSON(http.StatusOK, orz.Map{})
|
||||
}
|
||||
|
||||
// ListEvents 查询SSH登录事件
|
||||
|
||||
@@ -38,7 +38,7 @@ func (h *TamperHandler) UpdateTamperConfig(c echo.Context) error {
|
||||
})
|
||||
}
|
||||
|
||||
config, err := h.tamperService.UpdateConfig(agentID, req.Enabled, req.Paths)
|
||||
err := h.tamperService.UpdateConfig(c.Request().Context(), agentID, req.Enabled, req.Paths)
|
||||
if err != nil {
|
||||
h.logger.Error("更新防篡改配置失败", zap.Error(err), zap.String("agentId", agentID))
|
||||
return c.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
@@ -46,7 +46,7 @@ func (h *TamperHandler) UpdateTamperConfig(c echo.Context) error {
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, config)
|
||||
return c.JSON(http.StatusOK, orz.Map{})
|
||||
}
|
||||
|
||||
// GetTamperConfig 获取探针的防篡改配置
|
||||
@@ -54,7 +54,7 @@ func (h *TamperHandler) UpdateTamperConfig(c echo.Context) error {
|
||||
func (h *TamperHandler) GetTamperConfig(c echo.Context) error {
|
||||
agentID := c.Param("id")
|
||||
|
||||
config, err := h.tamperService.GetConfigByAgentID(agentID)
|
||||
config, err := h.tamperService.GetConfigByAgentID(c.Request().Context(), agentID)
|
||||
if err != nil {
|
||||
h.logger.Error("获取防篡改配置失败", zap.Error(err), zap.String("agentId", agentID))
|
||||
return c.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
|
||||
@@ -238,41 +238,3 @@ func (r *AgentRepo) FindAgentsWithTrafficReset(ctx context.Context) ([]models.Ag
|
||||
}
|
||||
return agents, nil
|
||||
}
|
||||
|
||||
// === 配置管理 ===
|
||||
|
||||
// GetTamperProtectConfig 获取防篡改保护配置
|
||||
func (r *AgentRepo) GetTamperProtectConfig(ctx context.Context, agentID string) (*models.TamperProtectConfigData, error) {
|
||||
var agent models.Agent
|
||||
err := r.db.WithContext(ctx).Select("tamper_protect_config").Where("id = ?", agentID).First(&agent).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := agent.TamperProtectConfig.Data()
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
// UpdateTamperProtectConfig 更新防篡改保护配置
|
||||
func (r *AgentRepo) UpdateTamperProtectConfig(ctx context.Context, agentID string, config *models.TamperProtectConfigData) error {
|
||||
return r.db.WithContext(ctx).Model(&models.Agent{}).
|
||||
Where("id = ?", agentID).
|
||||
Update("tamper_protect_config", config).Error
|
||||
}
|
||||
|
||||
// GetSSHLoginConfig 获取SSH登录监控配置
|
||||
func (r *AgentRepo) GetSSHLoginConfig(ctx context.Context, agentID string) (*models.SSHLoginConfigData, error) {
|
||||
var agent models.Agent
|
||||
err := r.db.WithContext(ctx).Select("ssh_login_config").Where("id = ?", agentID).First(&agent).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := agent.SSHLoginConfig.Data()
|
||||
return &data, nil
|
||||
}
|
||||
|
||||
// UpdateSSHLoginConfig 更新SSH登录监控配置
|
||||
func (r *AgentRepo) UpdateSSHLoginConfig(ctx context.Context, agentID string, config *models.SSHLoginConfigData) error {
|
||||
return r.db.WithContext(ctx).Model(&models.Agent{}).
|
||||
Where("id = ?", agentID).
|
||||
Update("ssh_login_config", config).Error
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@@ -11,7 +10,9 @@ import (
|
||||
"github.com/dushixiang/pika/internal/protocol"
|
||||
"github.com/dushixiang/pika/internal/repo"
|
||||
"github.com/dushixiang/pika/internal/websocket"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/datatypes"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -38,46 +39,33 @@ func NewSSHLoginService(logger *zap.Logger, db *gorm.DB, wsManager *websocket.Ma
|
||||
// === 配置管理 ===
|
||||
|
||||
// GetConfig 获取探针的配置
|
||||
func (s *SSHLoginService) GetConfig(agentID string) (*models.SSHLoginConfigData, error) {
|
||||
config, err := s.agentRepo.GetSSHLoginConfig(context.Background(), agentID)
|
||||
func (s *SSHLoginService) GetConfig(ctx context.Context, agentID string) (*models.SSHLoginConfigData, error) {
|
||||
agent, err := s.agentRepo.FindById(ctx, agentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config, nil
|
||||
config := agent.SSHLoginConfig.Data()
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// UpdateConfig 更新配置并下发到 Agent
|
||||
// 返回: config - 配置对象, configSent - 是否成功下发到 Agent, error - 错误信息
|
||||
func (s *SSHLoginService) UpdateConfig(ctx context.Context, agentID string, enabled bool) (*models.SSHLoginConfigData, bool, error) {
|
||||
// 检查探针是否存在
|
||||
agent, err := s.agentRepo.FindById(ctx, agentID)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf("探针不存在")
|
||||
}
|
||||
|
||||
// 返回: config - 配置对象, error - 错误信息
|
||||
func (s *SSHLoginService) UpdateConfig(ctx context.Context, agentID string, enabled bool) error {
|
||||
// 保存配置到数据库
|
||||
config := &models.SSHLoginConfigData{
|
||||
config := models.SSHLoginConfigData{
|
||||
Enabled: enabled,
|
||||
}
|
||||
|
||||
if err := s.agentRepo.UpdateSSHLoginConfig(ctx, agentID, config); err != nil {
|
||||
return nil, false, err
|
||||
var agentForUpdate = models.Agent{
|
||||
ID: agentID,
|
||||
SSHLoginConfig: datatypes.NewJSONType(config),
|
||||
}
|
||||
if err := s.agentRepo.UpdateById(ctx, &agentForUpdate); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 下发配置到 Agent
|
||||
configSent := false
|
||||
if agent.Status == 1 { // 仅在探针在线时尝试下发
|
||||
if err := s.sendConfigToAgent(agentID, enabled); err != nil {
|
||||
s.logger.Warn("下发SSH登录监控配置到Agent失败", zap.Error(err), zap.String("agentId", agentID))
|
||||
} else {
|
||||
s.logger.Info("成功下发SSH登录监控配置", zap.String("agentId", agentID), zap.Bool("enabled", enabled))
|
||||
configSent = true
|
||||
}
|
||||
} else {
|
||||
s.logger.Info("探针离线,配置将在下次连接时生效", zap.String("agentId", agentID))
|
||||
}
|
||||
|
||||
return config, configSent, nil
|
||||
return s.sendConfigToAgent(agentID, enabled)
|
||||
}
|
||||
|
||||
// sendConfigToAgent 下发配置到 Agent
|
||||
@@ -100,16 +88,12 @@ func (s *SSHLoginService) sendConfigToAgent(agentID string, enabled bool) error
|
||||
}
|
||||
|
||||
// HandleConfigResult 处理 Agent 上报的配置应用结果
|
||||
func (s *SSHLoginService) HandleConfigResult(agentID string, result protocol.SSHLoginConfigResult) error {
|
||||
func (s *SSHLoginService) HandleConfigResult(ctx context.Context, agentID string, result protocol.SSHLoginConfigResult) error {
|
||||
// 获取现有配置
|
||||
config, err := s.agentRepo.GetSSHLoginConfig(context.Background(), agentID)
|
||||
config, err := s.GetConfig(ctx, agentID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取配置失败: %w", err)
|
||||
}
|
||||
if config == nil {
|
||||
s.logger.Warn("收到配置应用结果,但配置不存在", zap.String("agentId", agentID))
|
||||
return nil
|
||||
}
|
||||
|
||||
// 更新配置应用状态
|
||||
status := "success"
|
||||
@@ -120,24 +104,11 @@ func (s *SSHLoginService) HandleConfigResult(agentID string, result protocol.SSH
|
||||
config.ApplyStatus = status
|
||||
config.ApplyMessage = result.Message
|
||||
|
||||
if err := s.agentRepo.UpdateSSHLoginConfig(context.Background(), agentID, config); err != nil {
|
||||
s.logger.Error("更新配置应用状态失败", zap.Error(err), zap.String("agentId", agentID))
|
||||
return err
|
||||
var agentForUpdate = models.Agent{
|
||||
ID: agentID,
|
||||
SSHLoginConfig: datatypes.NewJSONType(*config),
|
||||
}
|
||||
|
||||
// 记录日志
|
||||
if result.Success {
|
||||
s.logger.Info("Agent成功应用SSH登录监控配置",
|
||||
zap.String("agentId", agentID),
|
||||
zap.Bool("enabled", result.Enabled),
|
||||
zap.String("message", result.Message))
|
||||
} else {
|
||||
s.logger.Warn("Agent应用SSH登录监控配置失败",
|
||||
zap.String("agentId", agentID),
|
||||
zap.String("message", result.Message))
|
||||
}
|
||||
|
||||
return nil
|
||||
return s.agentRepo.UpdateById(ctx, &agentForUpdate)
|
||||
}
|
||||
|
||||
// === 事件处理 ===
|
||||
@@ -145,8 +116,8 @@ func (s *SSHLoginService) HandleConfigResult(agentID string, result protocol.SSH
|
||||
// HandleEvent 处理 Agent 上报的事件
|
||||
func (s *SSHLoginService) HandleEvent(ctx context.Context, agentID string, eventData protocol.SSHLoginEvent) error {
|
||||
// 检查是否启用监控
|
||||
config, err := s.agentRepo.GetSSHLoginConfig(ctx, agentID)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
config, err := s.GetConfig(ctx, agentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -3,15 +3,17 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/dushixiang/pika/internal/models"
|
||||
"github.com/dushixiang/pika/internal/protocol"
|
||||
"github.com/dushixiang/pika/internal/repo"
|
||||
"github.com/dushixiang/pika/internal/websocket"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/datatypes"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@@ -32,33 +34,56 @@ func NewTamperService(logger *zap.Logger, db *gorm.DB, wsManager *websocket.Mana
|
||||
}
|
||||
|
||||
// GetConfigByAgentID 获取探针的防篡改配置
|
||||
func (s *TamperService) GetConfigByAgentID(agentID string) (*models.TamperProtectConfigData, error) {
|
||||
config, err := s.agentRepo.GetTamperProtectConfig(context.Background(), agentID)
|
||||
func (s *TamperService) GetConfigByAgentID(ctx context.Context, agentID string) (*models.TamperProtectConfigData, error) {
|
||||
agent, err := s.agentRepo.FindById(ctx, agentID)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil // 没有配置不算错误
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return config, nil
|
||||
config := agent.TamperProtectConfig.Data()
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func (s *TamperService) UpdateConfigByAgentID(ctx context.Context, agentID string, config *models.TamperProtectConfigData) error {
|
||||
var agentForUpdate = models.Agent{
|
||||
ID: agentID,
|
||||
TamperProtectConfig: datatypes.NewJSONType(*config),
|
||||
}
|
||||
return s.agentRepo.UpdateById(ctx, &agentForUpdate)
|
||||
}
|
||||
|
||||
// UpdateConfig 更新探针的防篡改配置
|
||||
func (s *TamperService) UpdateConfig(agentID string, enabled bool, paths []string) (*models.TamperProtectConfigData, error) {
|
||||
func (s *TamperService) UpdateConfig(ctx context.Context, agentID string, enabled bool, paths []string) error {
|
||||
// 查找现有配置
|
||||
config, err := s.agentRepo.GetTamperProtectConfig(context.Background(), agentID)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, err
|
||||
config, err := s.GetConfigByAgentID(ctx, agentID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 获取旧的路径列表用于比对
|
||||
// 获取旧的路径列表和启用状态用于比对
|
||||
var oldPaths []string
|
||||
var wasEnabled bool
|
||||
if config != nil {
|
||||
oldPaths = config.Paths
|
||||
wasEnabled = config.Enabled
|
||||
}
|
||||
|
||||
// 计算新增和移除的路径
|
||||
added, removed := s.calculatePathDiff(oldPaths, paths)
|
||||
var added, removed []string
|
||||
|
||||
// 处理不同的状态转换场景
|
||||
if !enabled {
|
||||
// 场景1: 禁用防篡改功能,需要移除所有旧的路径配置
|
||||
removed = oldPaths
|
||||
added = []string{}
|
||||
// 注意: 不清空 paths,保留配置以便下次启用时使用
|
||||
} else if !wasEnabled {
|
||||
// 场景2: 从禁用切换到启用,所有路径都作为新增
|
||||
// 因为探针端已经移除了所有监控,需要重新添加
|
||||
added = paths
|
||||
removed = []string{}
|
||||
} else {
|
||||
// 场景3: 启用状态下的正常增量更新
|
||||
added, removed = s.calculatePathDiff(oldPaths, paths)
|
||||
}
|
||||
|
||||
// 创建或更新配置
|
||||
newConfig := &models.TamperProtectConfigData{
|
||||
@@ -67,12 +92,12 @@ func (s *TamperService) UpdateConfig(agentID string, enabled bool, paths []strin
|
||||
}
|
||||
|
||||
// 保存配置到数据库
|
||||
if err := s.agentRepo.UpdateTamperProtectConfig(context.Background(), agentID, newConfig); err != nil {
|
||||
return nil, err
|
||||
if err := s.UpdateConfigByAgentID(ctx, agentID, newConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 下发增量配置到探针
|
||||
if err := s.sendConfigToAgent(agentID, added, removed); err != nil {
|
||||
if err := s.sendIncrementalConfigToAgent(agentID, added, removed); err != nil {
|
||||
s.logger.Warn("下发防篡改配置到探针失败",
|
||||
zap.String("agentId", agentID),
|
||||
zap.Strings("added", added),
|
||||
@@ -87,7 +112,7 @@ func (s *TamperService) UpdateConfig(agentID string, enabled bool, paths []strin
|
||||
zap.Int("totalPaths", len(paths)))
|
||||
}
|
||||
|
||||
return newConfig, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// calculatePathDiff 计算路径的新增和移除
|
||||
@@ -120,8 +145,27 @@ func (s *TamperService) calculatePathDiff(oldPaths, newPaths []string) (added, r
|
||||
return added, removed
|
||||
}
|
||||
|
||||
// sendConfigToAgent 通过WebSocket下发配置到探针(增量更新)
|
||||
func (s *TamperService) sendConfigToAgent(agentID string, added, removed []string) error {
|
||||
// BuildInitialConfig 构建探针初始化时的配置(用于探针连接时)
|
||||
// 根据 enabled 状态决定发送的内容:
|
||||
// - enabled=true: 发送所有配置的路径作为新增
|
||||
// - enabled=false: 发送空配置
|
||||
func (s *TamperService) BuildInitialConfig(ctx context.Context, agentID string) (added, removed []string, err error) {
|
||||
config, err := s.GetConfigByAgentID(ctx, agentID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// 如果启用了防篡改,将所有路径作为新增发送
|
||||
if config != nil && config.Enabled && len(config.Paths) > 0 {
|
||||
return config.Paths, []string{}, nil
|
||||
}
|
||||
|
||||
// 未启用或没有配置,返回空
|
||||
return []string{}, []string{}, nil
|
||||
}
|
||||
|
||||
// sendIncrementalConfigToAgent 通过WebSocket下发配置到探针(增量更新)
|
||||
func (s *TamperService) sendIncrementalConfigToAgent(agentID string, added, removed []string) error {
|
||||
// 如果没有任何变更,不需要下发
|
||||
if len(added) == 0 && len(removed) == 0 {
|
||||
return nil
|
||||
@@ -165,3 +209,27 @@ func (s *TamperService) CleanupOldRecords(ctx context.Context) error {
|
||||
threshold := time.Now().AddDate(0, 0, -30).UnixMilli()
|
||||
return s.TamperEventRepo.DeleteOldEvents(ctx, threshold)
|
||||
}
|
||||
|
||||
func (s *TamperService) HandleConfigResult(ctx context.Context, agentID string, resp protocol.TamperProtectResponse) error {
|
||||
// 获取现有配置
|
||||
config, err := s.GetConfigByAgentID(ctx, agentID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取配置失败: %w", err)
|
||||
}
|
||||
|
||||
// 更新配置应用状态
|
||||
status := "success"
|
||||
if !resp.Success {
|
||||
status = "failed"
|
||||
}
|
||||
|
||||
config.ApplyStatus = status
|
||||
config.ApplyMessage = resp.Message
|
||||
|
||||
var agentForUpdate = models.Agent{
|
||||
ID: agentID,
|
||||
TamperProtectConfig: datatypes.NewJSONType(*config),
|
||||
}
|
||||
// 更新探针配置
|
||||
return s.agentRepo.UpdateById(ctx, &agentForUpdate)
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -344,17 +345,34 @@ func (p *Protector) initWatcher(ctx context.Context) error {
|
||||
|
||||
// addPath 添加目录保护(内部方法,不加锁)
|
||||
func (p *Protector) addPath(path string) error {
|
||||
// 设置不可变属性
|
||||
if err := p.setImmutable(path, true); err != nil {
|
||||
return fmt.Errorf("设置目录不可变属性失败: %w", err)
|
||||
// 检查路径是否存在
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("无法访问路径: %w", err)
|
||||
}
|
||||
|
||||
// 如果是目录,递归设置所有文件和子目录的不可变属性
|
||||
if info.IsDir() {
|
||||
if err := p.setImmutableRecursive(path, true); err != nil {
|
||||
return fmt.Errorf("递归设置目录不可变属性失败: %w", err)
|
||||
}
|
||||
} else {
|
||||
// 单个文件,直接设置不可变属性
|
||||
if err := p.setImmutable(path, true); err != nil {
|
||||
return fmt.Errorf("设置文件不可变属性失败: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加到监控
|
||||
if p.watcher != nil {
|
||||
if err := p.watcher.Add(path); err != nil {
|
||||
// 如果添加监控失败,尝试回滚不可变属性
|
||||
_ = p.setImmutable(path, false)
|
||||
return fmt.Errorf("添加目录到监控失败: %w", err)
|
||||
if info.IsDir() {
|
||||
_ = p.setImmutableRecursive(path, false)
|
||||
} else {
|
||||
_ = p.setImmutable(path, false)
|
||||
}
|
||||
return fmt.Errorf("添加路径到监控失败: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,14 +384,32 @@ func (p *Protector) removePath(path string) error {
|
||||
// 从监控中移除
|
||||
if p.watcher != nil {
|
||||
if err := p.watcher.Remove(path); err != nil {
|
||||
slog.Warn("从监控中移除目录失败", "error", err)
|
||||
slog.Warn("从监控中移除路径失败", "error", err)
|
||||
// 继续执行,不返回错误
|
||||
}
|
||||
}
|
||||
|
||||
// 移除不可变属性
|
||||
if err := p.setImmutable(path, false); err != nil {
|
||||
return fmt.Errorf("移除目录不可变属性失败: %w", err)
|
||||
// 检查路径是否存在
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
// 如果路径不存在,不报错,直接返回
|
||||
if os.IsNotExist(err) {
|
||||
slog.Info("路径已不存在,跳过移除属性", "path", path)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("无法访问路径: %w", err)
|
||||
}
|
||||
|
||||
// 如果是目录,递归移除所有文件和子目录的不可变属性
|
||||
if info.IsDir() {
|
||||
if err := p.setImmutableRecursive(path, false); err != nil {
|
||||
return fmt.Errorf("递归移除目录不可变属性失败: %w", err)
|
||||
}
|
||||
} else {
|
||||
// 单个文件,直接移除不可变属性
|
||||
if err := p.setImmutable(path, false); err != nil {
|
||||
return fmt.Errorf("移除文件不可变属性失败: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -519,6 +555,48 @@ func (p *Protector) checkImmutable(path string) (bool, error) {
|
||||
return IsAttr(f, FS_IMMUTABLE_FL)
|
||||
}
|
||||
|
||||
// setImmutableRecursive 递归设置或移除目录及其所有子文件/子目录的不可变属性
|
||||
func (p *Protector) setImmutableRecursive(rootPath string, immutable bool) error {
|
||||
var lastErr error
|
||||
var errCount int
|
||||
|
||||
// 使用 filepath.Walk 遍历目录树
|
||||
err := filepath.Walk(rootPath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
slog.Warn("访问路径失败", "path", path, "error", err)
|
||||
lastErr = err
|
||||
errCount++
|
||||
return nil // 继续处理其他文件
|
||||
}
|
||||
|
||||
// 设置不可变属性
|
||||
if setErr := p.setImmutable(path, immutable); setErr != nil {
|
||||
slog.Warn("设置不可变属性失败", "path", path, "immutable", immutable, "error", setErr)
|
||||
lastErr = setErr
|
||||
errCount++
|
||||
return nil // 继续处理其他文件
|
||||
}
|
||||
|
||||
if immutable {
|
||||
slog.Debug("已设置不可变属性", "path", path)
|
||||
} else {
|
||||
slog.Debug("已移除不可变属性", "path", path)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("遍历目录失败: %w", err)
|
||||
}
|
||||
|
||||
if errCount > 0 {
|
||||
return fmt.Errorf("部分文件处理失败,共 %d 个错误,最后一个错误: %w", errCount, lastErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setImmutable 设置或移除文件/目录的不可变属性
|
||||
func (p *Protector) setImmutable(path string, immutable bool) error {
|
||||
// 打开文件/目录
|
||||
|
||||
@@ -6,8 +6,6 @@ import {formatBytes, formatDateTime, formatPercentValue, formatUptime} from '@po
|
||||
import type {Agent, LatestMetrics} from '@/types';
|
||||
import CyberCard from "@portal/components/CyberCard.tsx";
|
||||
|
||||
type AccentVariant = 'blue' | 'emerald' | 'purple' | 'amber';
|
||||
|
||||
interface SystemInfoSectionProps {
|
||||
agent: Agent;
|
||||
latestMetrics: LatestMetrics | null;
|
||||
@@ -125,19 +123,18 @@ export const SystemInfoSection = ({agent, latestMetrics}: SystemInfoSectionProps
|
||||
];
|
||||
|
||||
// 如果配置了流量限额,添加流量统计信息到网络卡片
|
||||
if (agent && agent.trafficLimit && agent.trafficLimit > 0) {
|
||||
const trafficUsedPercent = ((agent.trafficUsed || 0) / agent.trafficLimit) * 100;
|
||||
const trafficRemaining = agent.trafficLimit - (agent.trafficUsed || 0);
|
||||
if (agent?.trafficStats?.enabled && agent.trafficStats.limit > 0) {
|
||||
const trafficUsedPercent = (agent.trafficStats.used / agent.trafficStats.limit) * 100;
|
||||
|
||||
networkMetrics.push({
|
||||
label: '流量限额',
|
||||
value: `${formatBytes(agent.trafficUsed || 0, 1)} / ${formatBytes(agent.trafficLimit, 1)} (${formatPercentValue(trafficUsedPercent)}%)`,
|
||||
value: `${formatBytes(agent.trafficStats.used, 1)} / ${formatBytes(agent.trafficStats.limit, 1)} (${formatPercentValue(trafficUsedPercent)}%)`,
|
||||
});
|
||||
|
||||
if (agent.trafficResetDay && agent.trafficResetDay > 0) {
|
||||
if (agent.trafficStats.resetDay > 0) {
|
||||
networkMetrics.push({
|
||||
label: '重置日期',
|
||||
value: `每月${agent.trafficResetDay}号`,
|
||||
value: `每月${agent.trafficStats.resetDay}号`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user