diff --git a/go.sum b/go.sum index fbd7ed7..87a8a55 100644 --- a/go.sum +++ b/go.sum @@ -58,6 +58,8 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.7.0 h1:JxUKI6+CVBgCO2WToKy/nQk0sS+amI9z9EjVmdaocj4= diff --git a/internal/handler/agent_handler.go b/internal/handler/agent_handler.go index 6595021..42bedac 100644 --- a/internal/handler/agent_handler.go +++ b/internal/handler/agent_handler.go @@ -16,13 +16,14 @@ type AgentHandler struct { tamperService *service.TamperService ddnsService *service.DDNSService sshLoginService *service.SSHLoginService + apiKeyService *service.ApiKeyService wsManager *ws.Manager upgrader websocket.Upgrader } func NewAgentHandler(logger *zap.Logger, agentService *service.AgentService, trafficService *service.TrafficService, metricService *service.MetricService, monitorService *service.MonitorService, tamperService *service.TamperService, - ddnsService *service.DDNSService, sshLoginService *service.SSHLoginService, wsManager *ws.Manager) *AgentHandler { + ddnsService *service.DDNSService, sshLoginService *service.SSHLoginService, apiKeyService *service.ApiKeyService, wsManager *ws.Manager) *AgentHandler { h := &AgentHandler{ logger: logger, @@ -33,6 +34,7 @@ func NewAgentHandler(logger *zap.Logger, agentService *service.AgentService, tra tamperService: tamperService, ddnsService: ddnsService, sshLoginService: sshLoginService, + apiKeyService: apiKeyService, wsManager: wsManager, } diff --git a/internal/handler/agent_install_handler.go b/internal/handler/agent_install_handler.go index 9aecc0b..4c5f6cf 100644 --- a/internal/handler/agent_install_handler.go +++ b/internal/handler/agent_install_handler.go @@ -22,6 +22,13 @@ func (h *AgentHandler) GetAgentVersion(c echo.Context) error { func (h *AgentHandler) DownloadAgent(c echo.Context) error { filename := c.Param("filename") + // 校验 API Key + apiKey := c.QueryParam("key") + if _, err := h.apiKeyService.ValidateApiKey(c.Request().Context(), apiKey); err != nil { + h.logger.Warn("download agent failed: invalid api key", zap.String("key", apiKey)) + return orz.NewError(401, "无效的 API 密钥") + } + // 从嵌入的文件系统读取 agentFile, err := pika.AgentFS().Open(fmt.Sprintf("pika-%s", filename)) if err != nil { @@ -135,7 +142,7 @@ detect_platform() { # 下载探针 download_agent() { - local download_url="` + serverUrl + `/api/agent/downloads/agent-$PLATFORM" + local download_url="` + serverUrl + `/api/agent/downloads/agent-$PLATFORM?key=` + token + `" local temp_file="/tmp/pika-agent-download" echo_info "正在下载探针..." diff --git a/internal/wire_gen.go b/internal/wire_gen.go index ac641d4..4e1c68f 100644 --- a/internal/wire_gen.go +++ b/internal/wire_gen.go @@ -43,7 +43,7 @@ func InitializeApp(logger *zap.Logger, db *gorm.DB, cfg *config.AppConfig) (*App tamperService := service.NewTamperService(logger, db, manager, notificationService) ddnsService := service.NewDDNSService(logger, db, propertyService, manager) sshLoginService := service.NewSSHLoginService(logger, db, manager, geoIPService, notificationService) - agentHandler := handler.NewAgentHandler(logger, agentService, trafficService, metricService, monitorService, tamperService, ddnsService, sshLoginService, manager) + agentHandler := handler.NewAgentHandler(logger, agentService, trafficService, metricService, monitorService, tamperService, ddnsService, sshLoginService, apiKeyService, manager) apiKeyHandler := handler.NewApiKeyHandler(logger, apiKeyService) alertService := service.NewAlertService(logger, db, propertyService, monitorService, notifier) alertHandler := handler.NewAlertHandler(logger, alertService) diff --git a/pkg/agent/config/config.go b/pkg/agent/config/config.go index e3aad2a..d75bd93 100644 --- a/pkg/agent/config/config.go +++ b/pkg/agent/config/config.go @@ -275,7 +275,7 @@ func (c *Config) GetDownloadURL() string { if runtime.GOOS == "windows" { filename += ".exe" } - return c.Endpoint() + "/api/agent/downloads/" + filename + return c.Endpoint() + "/api/agent/downloads/" + filename + "?key=" + c.Server.APIKey } func (c *Config) Endpoint() string { diff --git a/web/src/admin/pages/Agents/AgentInstall.tsx b/web/src/admin/pages/Agents/AgentInstall.tsx index 78b86b8..def105b 100644 --- a/web/src/admin/pages/Agents/AgentInstall.tsx +++ b/web/src/admin/pages/Agents/AgentInstall.tsx @@ -1,17 +1,17 @@ -import React, {type ReactElement, useCallback, useEffect, useMemo, useState} from 'react'; -import {Alert, App, Button, Card, Select, Space, Tabs, Typography} from 'antd'; -import {CopyIcon} from 'lucide-react'; -import {listApiKeys} from '@/api/apiKey.ts'; -import {getServerUrl} from '@/api/agent.ts'; -import type {ApiKey} from '@/types'; +import React, { type ReactElement, useCallback, useEffect, useMemo, useState } from 'react'; +import { Alert, App, Button, Card, Select, Space, Tabs, Typography } from 'antd'; +import { CopyIcon } from 'lucide-react'; +import { listApiKeys } from '@/api/apiKey.ts'; +import { getServerUrl } from '@/api/agent.ts'; +import type { ApiKey } from '@/types'; import linuxPng from '../../assets/os/linux.png'; import applePng from '../../assets/os/apple.png'; import windowsPng from '../../assets/os/win11.png'; -import {useNavigate} from "react-router-dom"; +import { useNavigate } from "react-router-dom"; import copy from 'copy-to-clipboard'; -const {Paragraph, Text} = Typography; -const {TabPane} = Tabs; +const { Paragraph, Text } = Typography; +const { TabPane } = Tabs; // 常量定义 const AGENT_NAME = 'pika-agent'; @@ -42,7 +42,7 @@ const AgentInstall = () => { // 服务器地址相关状态 const [backendServerUrl, setBackendServerUrl] = useState(''); - const {message} = App.useApp(); + const { message } = App.useApp(); const navigate = useNavigate(); const frontendUrl = useMemo(() => window.location.origin, []); @@ -86,37 +86,37 @@ const AgentInstall = () => { const osConfigs: Record = useMemo(() => ({ 'linux-amd64': { name: 'Linux (amd64)', - icon: Linux, + icon: Linux, downloadUrl: '/api/agent/downloads/agent-linux-amd64', }, 'linux-arm64': { name: 'Linux (arm64)', - icon: Linux, + icon: Linux, downloadUrl: '/api/agent/downloads/agent-linux-arm64', }, 'linux-loong64': { name: 'Linux (loongarch64)', - icon: Linux, + icon: Linux, downloadUrl: '/api/agent/downloads/agent-linux-loong64', }, 'darwin-amd64': { name: 'macOS (amd64)', - icon: macOS, + icon: macOS, downloadUrl: '/api/agent/downloads/agent-darwin-amd64', }, 'darwin-arm64': { name: 'macOS (arm64)', - icon: macOS, + icon: macOS, downloadUrl: '/api/agent/downloads/agent-darwin-arm64', }, 'windows-amd64': { name: 'Windows (amd64)', - icon: Windows, + icon: Windows, downloadUrl: '/api/agent/downloads/agent-windows-amd64.exe', }, 'windows-arm64': { name: 'Windows (arm64)', - icon: Windows, + icon: Windows, downloadUrl: '/api/agent/downloads/agent-windows-arm64.exe', }, }), []); @@ -158,7 +158,7 @@ const AgentInstall = () => {
当前访问地址: {frontendUrl} -
+
后端检测地址: {backendServerUrl}
@@ -170,7 +170,7 @@ const AgentInstall = () => {
Nginx 配置示例:
-{`location / {
+                                {`location / {
     proxy_pass http://backend;
     proxy_set_header X-Forwarded-Proto $scheme;
     proxy_set_header X-Forwarded-Host $host;
@@ -182,7 +182,7 @@ const AgentInstall = () => {
                         
Caddy 配置示例:
-{`reverse_proxy backend:8080 {
+                                {`reverse_proxy backend:8080 {
     header_up X-Forwarded-Proto {scheme}
     header_up X-Forwarded-Host {host}
 }`}
@@ -191,7 +191,7 @@ const AgentInstall = () => {
                         
Traefik 配置说明:
-{`# Traefik 默认会自动添加 X-Forwarded-* 头部
+                                {`# Traefik 默认会自动添加 X-Forwarded-* 头部
 # 无需额外配置`}
                             
@@ -240,8 +240,8 @@ const AgentInstall = () => { // 一键安装组件 const InstallByOneClick = useCallback(() => ( - - + + { - - + + ), [ServerUrlChecker, ApiChooser, installCommand, copyToClipboard, selectedApiKey]); @@ -297,7 +297,7 @@ ${agentCmd} version`; }, []); // 服务管理命令组件 - const ServiceHelper = useCallback(({os}: { os: string }) => ( + const ServiceHelper = useCallback(({ os }: { os: string }) => ( 注册完成后,您可以使用以下命令管理探针服务: @@ -335,10 +335,10 @@ ${agentCmd} version`; { title: '1. 下载探针', command: `# 使用 PowerShell 下载 -Invoke-WebRequest -Uri "${backendServerUrl}${config.downloadUrl}" -OutFile "${AGENT_NAME_EXE}" +Invoke-WebRequest -Uri "${backendServerUrl}${config.downloadUrl}?key=${selectedApiKey}" -OutFile "${AGENT_NAME_EXE}" # 或者使用浏览器直接下载 -# ${backendServerUrl}${config.downloadUrl}` +# ${backendServerUrl}${config.downloadUrl}?key=${selectedApiKey}` }, { title: '2. 注册探针', @@ -355,10 +355,10 @@ Invoke-WebRequest -Uri "${backendServerUrl}${config.downloadUrl}" -OutFile "${AG { title: '1. 下载探针', command: `# 使用 wget 下载 -wget ${backendServerUrl}${config.downloadUrl} -O ${AGENT_NAME} +wget "${backendServerUrl}${config.downloadUrl}?key=${selectedApiKey}" -O ${AGENT_NAME} # 或使用 curl 下载 -curl -L ${backendServerUrl}${config.downloadUrl} -o ${AGENT_NAME}` +curl -L "${backendServerUrl}${config.downloadUrl}?key=${selectedApiKey}" -o ${AGENT_NAME}` }, { title: '2. 赋予执行权限', @@ -382,8 +382,8 @@ curl -L ${backendServerUrl}${config.downloadUrl} -o ${AGENT_NAME}` // 手动安装组件 const InstallByManual = useCallback(() => ( - - + + setSelectedOS(key as OSType)} @@ -404,7 +404,7 @@ curl -L ${backendServerUrl}${config.downloadUrl} -o ${AGENT_NAME}` {getManualInstallSteps(key as OSType).map((step, index) => (
{step.title} + className="block mb-2 text-gray-900 dark:text-slate-100">{step.title}
                                                 {step.command}
@@ -412,9 +412,9 @@ curl -L ${backendServerUrl}${config.downloadUrl} -o ${AGENT_NAME}`