fix: 正确识别 Agent 安装与审计失败
- 下载脚本失败时立即终止安装并提示 WAF 拦截 - Agent 服务未运行时不再计为安装成功 - 审计列表兼容后端 status 成功失败字段
This commit is contained in:
1 parent
7553ef54de
commit
827d9a31cd
3 files changed
+75
-25
No files matched your search
@@ -180,6 +180,34 @@ func shellQuote(value string) string {
|
||||
return "'" + strings.ReplaceAll(value, "'", "'\"'\"'") + "'"
|
||||
}
|
||||
|
||||
// agentInstallCommand 先完整下载脚本再执行,确保 curl 失败能正确传递退出状态。 // Logan.Li
|
||||
func agentInstallCommand(serverURL, token string) string {
|
||||
scriptURL := shellQuote(serverURL + "/agent/install.sh")
|
||||
serverArg := shellQuote(serverURL)
|
||||
tokenArg := shellQuote(token)
|
||||
return fmt.Sprintf(
|
||||
`tmp=$(mktemp /tmp/aiops-agent-install.XXXXXX) && trap 'rm -f "$tmp"' EXIT && curl -fsSL -o "$tmp" %s && if [ "$(id -u)" -eq 0 ]; then bash "$tmp" --server %s --token %s; else sudo -n bash "$tmp" --server %s --token %s; fi`,
|
||||
scriptURL, serverArg, tokenArg, serverArg, tokenArg,
|
||||
)
|
||||
}
|
||||
|
||||
// agentInstallErrorMessage 将常见安装失败转换为可操作的中文提示。 // Logan.Li
|
||||
func agentInstallErrorMessage(serverURL, output string) string {
|
||||
lowerOutput := strings.ToLower(output)
|
||||
switch {
|
||||
case strings.Contains(lowerOutput, "403") || strings.Contains(lowerOutput, "forbidden"):
|
||||
return "域名代理或 WAF 拒绝 Agent 下载(HTTP 403),请为 /agent/* 关闭人机验证或加入机器访问白名单"
|
||||
case strings.Contains(lowerOutput, "sudo: a password is required") || strings.Contains(lowerOutput, "需要密码"):
|
||||
return "目标主机 SSH 用户没有免密 sudo 权限,请使用 root 账号或配置 passwordless sudo"
|
||||
case strings.Contains(lowerOutput, "curl") && (strings.Contains(lowerOutput, "not found") || strings.Contains(lowerOutput, "未找到")):
|
||||
return "目标主机未安装 curl"
|
||||
case strings.Contains(lowerOutput, "下载失败") || strings.Contains(lowerOutput, "failed to connect") || strings.Contains(lowerOutput, "connection refused"):
|
||||
return "目标主机无法下载安装脚本,请检查后端地址 " + serverURL + " 是否可达"
|
||||
default:
|
||||
return "Agent 安装命令执行失败"
|
||||
}
|
||||
}
|
||||
|
||||
// detectAgentVersion SSH 到目标主机读取已安装 Agent 的真实版本号 // Logan.Li
|
||||
func detectAgentVersion(client *ssh.Client) string {
|
||||
verOut, _ := runSSHCommand(client, "/usr/local/bin/aiops-agent version 2>/dev/null || /usr/local/bin/aiops-agent -v 2>/dev/null || echo ''")
|
||||
@@ -298,25 +326,13 @@ func (h *AgentInstallHandler) InstallAgent(c *gin.Context) {
|
||||
token = c.Query("token")
|
||||
}
|
||||
|
||||
// 4. 构造安装命令:root 用户直接 bash,非 root 用 sudo -n(无密码才执行,避免挂死)// Logan.Li
|
||||
installCmd := fmt.Sprintf(
|
||||
`if [ "$(id -u)" -eq 0 ]; then curl -fsSL %s/agent/install.sh | bash -s -- --server %s --token %s; else curl -fsSL %s/agent/install.sh | sudo -n bash -s -- --server %s --token %s; fi`,
|
||||
shellQuote(serverURL), shellQuote(serverURL), shellQuote(token),
|
||||
shellQuote(serverURL), shellQuote(serverURL), shellQuote(token),
|
||||
)
|
||||
// 4. 先下载脚本再执行,curl/WAF 失败时不会被管道末端 bash 的退出码掩盖。 // Logan.Li
|
||||
installCmd := agentInstallCommand(serverURL, token)
|
||||
|
||||
// 5. 执行安装
|
||||
installOut, err := runSSHCommand(client, installCmd)
|
||||
if err != nil {
|
||||
msg := "安装命令执行失败"
|
||||
lowerOut := strings.ToLower(installOut)
|
||||
if strings.Contains(lowerOut, "sudo: a password is required") || strings.Contains(lowerOut, "需要密码") {
|
||||
msg = "目标主机 SSH 用户没有免密 sudo 权限,请使用 root 账号或配置 passwordless sudo"
|
||||
} else if strings.Contains(lowerOut, "curl") && (strings.Contains(lowerOut, "not found") || strings.Contains(lowerOut, "未找到")) {
|
||||
msg = "目标主机未安装 curl"
|
||||
} else if strings.Contains(lowerOut, "下载失败") || strings.Contains(lowerOut, "failed to connect") || strings.Contains(lowerOut, "connection refused") {
|
||||
msg = "目标主机无法下载安装脚本,请检查后端地址 " + serverURL + " 是否可达"
|
||||
}
|
||||
msg := agentInstallErrorMessage(serverURL, installOut)
|
||||
h.recordAgentAudit(ac, "install", resource, resourceID, 0,
|
||||
fmt.Sprintf("server=%s arch=%s", serverURL, arch), installOut, msg)
|
||||
c.JSON(http.StatusOK, model.Response{
|
||||
@@ -330,6 +346,13 @@ func (h *AgentInstallHandler) InstallAgent(c *gin.Context) {
|
||||
// 5. 验证安装是否成功
|
||||
verifyOut, _ := runSSHCommand(client, "systemctl is-active aiops-agent 2>/dev/null || echo 'not_found'")
|
||||
installed := contains(verifyOut, "active") || contains(verifyOut, "running")
|
||||
if !installed {
|
||||
msg := "安装脚本已执行,但 Agent 服务未运行"
|
||||
h.recordAgentAudit(ac, "install", resource, resourceID, 0,
|
||||
fmt.Sprintf("server=%s arch=%s", serverURL, arch), installOut+"\n"+verifyOut, msg)
|
||||
c.JSON(http.StatusOK, model.Response{Code: 500, Message: msg, Data: gin.H{"output": installOut, "verify": verifyOut}})
|
||||
return
|
||||
}
|
||||
|
||||
var agentVer string
|
||||
if installed {
|
||||
@@ -384,11 +407,7 @@ func (h *AgentInstallHandler) installOne(server model.Server, serverURL, token s
|
||||
|
||||
// 不强制 server.Status==running:只要 SSH 可达即可安装(DB 状态可能滞后于实际开机状态) // Logan.Li
|
||||
|
||||
installCmd := fmt.Sprintf(
|
||||
`if [ "$(id -u)" -eq 0 ]; then curl -fsSL %s/agent/install.sh | bash -s -- --server %s --token %s; else curl -fsSL %s/agent/install.sh | sudo -n bash -s -- --server %s --token %s; fi`,
|
||||
shellQuote(serverURL), shellQuote(serverURL), shellQuote(token),
|
||||
shellQuote(serverURL), shellQuote(serverURL), shellQuote(token),
|
||||
)
|
||||
installCmd := agentInstallCommand(serverURL, token)
|
||||
|
||||
installOut, err := runSSHCommand(client, installCmd)
|
||||
if err != nil {
|
||||
@@ -397,6 +416,9 @@ func (h *AgentInstallHandler) installOne(server model.Server, serverURL, token s
|
||||
|
||||
verifyOut, _ := runSSHCommand(client, "systemctl is-active aiops-agent 2>/dev/null || echo 'not_found'")
|
||||
installed := contains(verifyOut, "active") || contains(verifyOut, "running")
|
||||
if !installed {
|
||||
return false, installOut, verifyOut, fmt.Errorf("安装脚本已执行,但 Agent 服务未运行")
|
||||
}
|
||||
|
||||
if installed {
|
||||
// 只更新在线状态;版本号仅在能读到真实版本时才写入,未上报时不写假版本 // Logan.Li
|
||||
@@ -473,8 +495,13 @@ func (h *AgentInstallHandler) BatchInstallAgent(c *gin.Context) {
|
||||
rid := strconv.Itoa(int(server.ID))
|
||||
reqDetail := fmt.Sprintf("server=%s host=%s", serverURL, res)
|
||||
if err != nil {
|
||||
log.Printf("[BatchInstall] %s (%s) 安装失败: %v", server.Hostname, server.IP, err)
|
||||
h.recordAgentAudit(ac, "install", res, rid, 0, reqDetail, installOut, truncateAuditText(err.Error(), 500))
|
||||
errMessage := agentInstallErrorMessage(serverURL, installOut)
|
||||
if errMessage == "Agent 安装命令执行失败" {
|
||||
errMessage = err.Error()
|
||||
}
|
||||
log.Printf("[BatchInstall] %s (%s) 安装失败: %s", server.Hostname, server.IP, errMessage)
|
||||
respDetail := fmt.Sprintf("=== 安装输出 ===\n%s\n=== 服务状态 ===\n%s", installOut, verifyOut)
|
||||
h.recordAgentAudit(ac, "install", res, rid, 0, reqDetail, respDetail, truncateAuditText(errMessage, 500))
|
||||
} else {
|
||||
successCount++
|
||||
respDetail := fmt.Sprintf("=== 安装输出 ===\n%s\n=== 服务状态 ===\n%s", installOut, verifyOut)
|
||||
|
||||
Reference in new issue
Block a user