281 lines
8.6 KiB
Go
281 lines
8.6 KiB
Go
package handler
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/dushixiang/pika/internal/protocol"
|
|
ws "github.com/dushixiang/pika/internal/websocket"
|
|
"github.com/gorilla/websocket"
|
|
"github.com/labstack/echo/v4"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// HandleWebSocket 处理WebSocket连接
|
|
func (h *AgentHandler) HandleWebSocket(c echo.Context) error {
|
|
conn, err := h.upgrader.Upgrade(c.Response(), c.Request(), nil)
|
|
if err != nil {
|
|
h.logger.Error("failed to upgrade websocket", zap.Error(err))
|
|
return err
|
|
}
|
|
|
|
registerReq, err := h.readRegisterRequest(conn)
|
|
if err != nil {
|
|
conn.Close()
|
|
return err
|
|
}
|
|
|
|
// 注册探针 - 使用独立的context,不依赖HTTP请求的context
|
|
agent, err := h.agentService.RegisterAgent(context.Background(), c.RealIP(), ®isterReq.AgentInfo, registerReq.ApiKey)
|
|
if err != nil {
|
|
// 发送注册失败响应
|
|
h.sendRegisterError(conn, err.Error())
|
|
conn.Close()
|
|
return err
|
|
}
|
|
|
|
defer func() {
|
|
h.markAgentOffline(agent.ID)
|
|
}()
|
|
|
|
// 发送注册成功响应
|
|
if err := h.sendRegisterSuccess(conn, agent.ID); err != nil {
|
|
h.logger.Error("failed to send register ack", zap.Error(err))
|
|
conn.Close()
|
|
return err
|
|
}
|
|
|
|
// 下发防篡改配置
|
|
if err := h.sendTamperConfig(conn, agent.ID); err != nil {
|
|
h.logger.Error("failed to send tamper config", zap.Error(err))
|
|
// 配置下发失败不中断连接,只记录日志
|
|
}
|
|
// 下发SSH登录监控配置
|
|
if err := h.sendSSHLoginConfig(conn, agent.ID); err != nil {
|
|
h.logger.Error("failed to send ssh login config", zap.Error(err))
|
|
// 配置下发失败不中断连接,只记录日志
|
|
}
|
|
|
|
// 创建客户端并注册到管理器
|
|
client := h.newClient(agent.ID, conn)
|
|
|
|
h.wsManager.Register(client)
|
|
|
|
// 启动读写协程
|
|
go client.WritePump()
|
|
client.ReadPump(context.Background())
|
|
return nil
|
|
}
|
|
|
|
// handleWebSocketMessage 处理WebSocket消息
|
|
func (h *AgentHandler) handleWebSocketMessage(ctx context.Context, agentID string, messageType string, data json.RawMessage) error {
|
|
switch protocol.MessageType(messageType) {
|
|
case protocol.MessageTypeHeartbeat:
|
|
return h.handleHeartbeatMessage(ctx, agentID)
|
|
|
|
case protocol.MessageTypeMetrics:
|
|
return h.handleMetricsMessage(ctx, agentID, data)
|
|
|
|
case protocol.MessageTypeCommandResp:
|
|
return h.handleCommandResponseMessage(ctx, agentID, data)
|
|
|
|
case protocol.MessageTypeTamperEvent:
|
|
return h.handleTamperEventMessage(ctx, agentID, data)
|
|
|
|
case protocol.MessageTypeDDNSIPReport:
|
|
return h.handleDDNSIPReportMessage(ctx, agentID, data)
|
|
|
|
case protocol.MessageTypeSSHLoginEvent:
|
|
return h.handleSSHLoginEventMessage(ctx, agentID, data)
|
|
|
|
case protocol.MessageTypeSSHLoginConfigResult:
|
|
return h.handleSSHLoginConfigResultMessage(ctx, agentID, data)
|
|
|
|
case protocol.MessageTypeTamperProtect:
|
|
return h.handleTamperProtectMessage(ctx, agentID, data)
|
|
|
|
default:
|
|
h.logger.Warn("unknown message type", zap.String("type", messageType))
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (h *AgentHandler) readRegisterRequest(conn *websocket.Conn) (*protocol.RegisterRequest, error) {
|
|
conn.SetReadDeadline(time.Now().Add(10 * time.Second))
|
|
_, message, err := conn.ReadMessage()
|
|
if err != nil {
|
|
h.logger.Error("failed to read register message", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
conn.SetReadDeadline(time.Time{})
|
|
|
|
var msg protocol.InputMessage
|
|
if err := json.Unmarshal(message, &msg); err != nil {
|
|
h.logger.Error("failed to parse register message", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
if msg.Type != protocol.MessageTypeRegister {
|
|
h.logger.Error("first message must be register", zap.String("type", string(msg.Type)))
|
|
return nil, echo.NewHTTPError(http.StatusBadRequest, "首条消息必须是注册消息")
|
|
}
|
|
|
|
var registerReq protocol.RegisterRequest
|
|
if err := json.Unmarshal(msg.Data, ®isterReq); err != nil {
|
|
h.logger.Error("failed to parse register request", zap.Error(err))
|
|
return nil, err
|
|
}
|
|
|
|
return ®isterReq, nil
|
|
}
|
|
|
|
func (h *AgentHandler) markAgentOffline(agentID string) {
|
|
_ = h.agentService.UpdateAgentStatus(context.Background(), agentID, 0)
|
|
}
|
|
|
|
func (h *AgentHandler) newClient(agentID string, conn *websocket.Conn) *ws.Client {
|
|
return &ws.Client{
|
|
ID: agentID,
|
|
Conn: conn,
|
|
Send: make(chan []byte, 256),
|
|
Manager: h.wsManager,
|
|
LastActive: time.Now(),
|
|
}
|
|
}
|
|
|
|
func (h *AgentHandler) handleHeartbeatMessage(ctx context.Context, agentID string) error {
|
|
return h.agentService.UpdateAgentStatus(ctx, agentID, 1)
|
|
}
|
|
|
|
func (h *AgentHandler) handleMetricsMessage(ctx context.Context, agentID string, data json.RawMessage) error {
|
|
var metricsWrapper protocol.MetricsPayload
|
|
if err := json.Unmarshal(data, &metricsWrapper); err != nil {
|
|
return err
|
|
}
|
|
metricsData, err := json.Marshal(metricsWrapper.Data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return h.metricService.HandleMetricData(ctx, agentID, string(metricsWrapper.Type), metricsData, metricsWrapper.Timestamp)
|
|
}
|
|
|
|
func (h *AgentHandler) handleCommandResponseMessage(ctx context.Context, agentID string, data json.RawMessage) error {
|
|
var cmdResp protocol.CommandResponse
|
|
if err := json.Unmarshal(data, &cmdResp); err != nil {
|
|
return err
|
|
}
|
|
return h.agentService.HandleCommandResponse(ctx, agentID, &cmdResp)
|
|
}
|
|
|
|
func (h *AgentHandler) handleTamperEventMessage(ctx context.Context, agentID string, data json.RawMessage) error {
|
|
var eventData protocol.TamperEventData
|
|
if err := json.Unmarshal(data, &eventData); err != nil {
|
|
h.logger.Error("failed to unmarshal tamper event", zap.Error(err))
|
|
return err
|
|
}
|
|
return h.tamperService.CreateEvent(ctx, agentID, &eventData)
|
|
}
|
|
|
|
func (h *AgentHandler) handleDDNSIPReportMessage(ctx context.Context, agentID string, data json.RawMessage) error {
|
|
var ipReport protocol.DDNSIPReportData
|
|
if err := json.Unmarshal(data, &ipReport); err != nil {
|
|
h.logger.Error("failed to unmarshal ddns ip report", zap.Error(err))
|
|
return err
|
|
}
|
|
return h.ddnsService.HandleIPReport(ctx, agentID, &ipReport)
|
|
}
|
|
|
|
func (h *AgentHandler) handleSSHLoginEventMessage(ctx context.Context, agentID string, data json.RawMessage) error {
|
|
var eventData protocol.SSHLoginEvent
|
|
if err := json.Unmarshal(data, &eventData); err != nil {
|
|
h.logger.Error("failed to unmarshal ssh login event", zap.Error(err))
|
|
return err
|
|
}
|
|
return h.sshLoginService.HandleEvent(ctx, agentID, eventData)
|
|
}
|
|
|
|
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(ctx, agentID, resultData)
|
|
}
|
|
|
|
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
|
|
}
|
|
return h.tamperService.HandleConfigResult(ctx, agentID, protectResp)
|
|
}
|
|
|
|
// sendRegisterSuccess 发送注册成功响应
|
|
func (h *AgentHandler) sendRegisterSuccess(conn *websocket.Conn, agentID string) error {
|
|
resp := protocol.RegisterResponse{
|
|
AgentID: agentID,
|
|
Status: "success",
|
|
}
|
|
return conn.WriteJSON(protocol.OutboundMessage{
|
|
Type: protocol.MessageTypeRegisterAck,
|
|
Data: resp,
|
|
})
|
|
}
|
|
|
|
// sendRegisterError 发送注册失败响应
|
|
func (h *AgentHandler) sendRegisterError(conn *websocket.Conn, errMsg string) error {
|
|
resp := protocol.RegisterResponse{
|
|
Status: "error",
|
|
Message: errMsg,
|
|
}
|
|
|
|
return conn.WriteJSON(protocol.OutboundMessage{
|
|
Type: protocol.MessageTypeRegisterErr,
|
|
Data: resp,
|
|
})
|
|
}
|
|
|
|
// sendTamperConfig 发送防篡改配置(探针初始化时发送完整配置作为新增)
|
|
func (h *AgentHandler) sendTamperConfig(conn *websocket.Conn, agentID string) error {
|
|
// 使用 TamperService 构建初始配置(复用逻辑,会自动判断 enabled 状态)
|
|
added, removed, err := h.tamperService.BuildInitialConfig(context.Background(), agentID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 使用增量配置格式
|
|
configData := protocol.TamperProtectConfig{
|
|
Added: added,
|
|
Removed: removed,
|
|
}
|
|
|
|
msgData, err := json.Marshal(protocol.OutboundMessage{
|
|
Type: protocol.MessageTypeTamperProtect,
|
|
Data: configData,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return conn.WriteMessage(websocket.TextMessage, msgData)
|
|
}
|
|
|
|
func (h *AgentHandler) sendSSHLoginConfig(conn *websocket.Conn, agentID string) error {
|
|
config, err := h.sshLoginService.GetConfig(context.Background(), agentID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
msgData, err := json.Marshal(protocol.SSHLoginConfig{
|
|
Enabled: config.Enabled,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return conn.WriteMessage(websocket.TextMessage, msgData)
|
|
}
|