From db3d259a0a5ec2886bd99fc9cf4dc6051d864bef Mon Sep 17 00:00:00 2001 From: zeaslity Date: Fri, 28 Feb 2025 23:58:38 +0800 Subject: [PATCH] Enhance Proxy and Configuration Management - Implemented comprehensive VMESS proxy installation with dynamic configuration - Added support for Xray installation and configuration generation - Introduced hostname normalization with city, architecture, and IP-based naming - Updated proxy commands to include VMESS and VLESS subcommands - Improved configuration management with NormalizeConfig method - Enhanced logging and error handling for proxy-related operations --- agent-wdd/cmd/Info.go | 4 + agent-wdd/cmd/Proxy.go | 261 ++++++++++++++++++++++++- agent-wdd/cmd/root.go | 2 +- agent-wdd/cmd/xray/vmessTemplate.go | 38 ++++ agent-wdd/config/Config.go | 43 ++++ agent-wdd/test/one-click-build-run.ps1 | 37 ++-- agent-wdd/test/run_test.sh | 14 +- agent-wdd/utils/PrintUtils.go | 6 +- 8 files changed, 376 insertions(+), 29 deletions(-) create mode 100644 agent-wdd/cmd/xray/vmessTemplate.go diff --git a/agent-wdd/cmd/Info.go b/agent-wdd/cmd/Info.go index fde87c3..90f106a 100644 --- a/agent-wdd/cmd/Info.go +++ b/agent-wdd/cmd/Info.go @@ -75,11 +75,15 @@ func addInfoSubcommands(cmd *cobra.Command) { Use: "all", Short: "主机全部相关的信息", Run: func(cmd *cobra.Command, args []string) { + // 执行所有info cpu.Run(cmd, args) os.Run(cmd, args) memory.Run(cmd, args) network.Run(cmd, args) disk.Run(cmd, args) + + // 归一化 + config.ConfigCache.NormalizeConfig() }, } diff --git a/agent-wdd/cmd/Proxy.go b/agent-wdd/cmd/Proxy.go index 43fb15c..5e174c0 100644 --- a/agent-wdd/cmd/Proxy.go +++ b/agent-wdd/cmd/Proxy.go @@ -1,7 +1,17 @@ package cmd import ( + "agent-wdd/cmd/xray" + "agent-wdd/config" + "agent-wdd/log" + "agent-wdd/op" + "agent-wdd/utils" + "bytes" + "encoding/json" "fmt" + "os" + "text/template" + "github.com/spf13/cobra" ) @@ -16,23 +26,256 @@ func addProxySubcommands(cmd *cobra.Command) { Use: "install", Short: "安装Xray", Run: func(cmd *cobra.Command, args []string) { - fmt.Println("Installing Xray...") + log.Info("Installing Xray...") + + installXray() }, }, // 其他xray子命令... ) + vmessCmd := &cobra.Command{ + Use: "vmess [port]", + Short: "VMESS代理安装", + Run: func(cmd *cobra.Command, args []string) { + log.Info("Setting up VMESS proxy...") + + var port string + + if len(args) == 0 { + log.Info("no port provided, using default port 443") + port = "443" + } else { + port = args[0] + } + + // 执行info all命令 获得归一化的名字 高耦合方案 不推荐 + // for _, command := range cmd.Parent().Parent().Commands() { + // if command.Name() == "info" { + // command.Run(command, []string{"all"}) + // } + // } + + // 2. 调用 info all 命令 + if err := executeInfoAll(cmd); err != nil { + fmt.Printf("Error executing info all: %v\n", err) + } + + installVmess(port) + }, + } + + vlessCmd := &cobra.Command{ + Use: "vless [port]", + Short: "VLESS代理安装", + Run: func(cmd *cobra.Command, args []string) { + log.Info("Setting up VLESS proxy...") + + // var port string + + // if len(args) == 0 { + // log.Info("no port provided, using default port 443") + // port = "443" + // } + }, + } cmd.AddCommand( xrayCmd, - &cobra.Command{ - Use: "vmess", - Short: "设置VMESS代理", - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("Setting up VMESS proxy...") - }, - }, + vmessCmd, + vlessCmd, // 其他proxy子命令... ) } -// 其他命令结构类似,根据需求补充完整... +func executeInfoAll(cmd *cobra.Command) any { + // 获取根命令 + rootCmd := cmd.Root() + + // 创建临时上下文 + infoCmd, _, err := rootCmd.Find([]string{"info", "all"}) + if err != nil { + return fmt.Errorf("command not found: %w", err) + } + + // 克隆命令避免污染原始配置 + clonedCmd := cloneCommand(infoCmd) + + // 重置命令状态 + clonedCmd.SetArgs([]string{}) + clonedCmd.SilenceErrors = true + clonedCmd.SilenceUsage = true + + // 执行命令 + return clonedCmd.Execute() +} + +// 深度克隆命令的工具函数 +func cloneCommand(cmd *cobra.Command) *cobra.Command { + cloned := &cobra.Command{ + Use: cmd.Use, + Run: cmd.Run, + PreRun: cmd.PreRun, + PostRun: cmd.PostRun, + } + cloned.Flags().AddFlagSet(cmd.Flags()) + return cloned +} + +// VmessConfig 定义模板数据模型 +type VmessConfig struct { + PORT string + UUID string +} + +type VmessClientConfig struct { + ServerNodeName string + ServerNodeAddress string + VmessConfig +} + +// 安装VMESS代理 最简单快速的模式 +func installVmess(port string) (bool, []string) { + + // 检查是否安装了xray + if !op.CommandExistsByPath("xray") { + log.Error("Xray is not installed, please install xray first") + return false, []string{"Xray is not installed, please install xray first"} + } + + // 构建配置 + // 创建新模板并解析 + tmpl, err := template.New("vmessConfig").Parse(xray.VmessServerTemplate) + if err != nil { + return false, []string{err.Error()} + } + + // 准备模板数据 + + // 执行 xray uuid 拿到返回值 + ok, resultLog := op.SingleLineCommandExecutor([]string{"xray", "uuid"}) + if !ok { + return false, resultLog + } + + if len(resultLog) == 0 { + return false, []string{ + "xray uuid generate error ! cant not get the uuid", + } + } + uuid := resultLog[0] + + vmessConfig := VmessConfig{ + PORT: port, + UUID: uuid, + } + + // 执行模板渲染 + var result bytes.Buffer + if err := tmpl.Execute(&result, vmessConfig); err != nil { + return false, []string{ + err.Error(), + } + } + + // 将result写入到 /usr/local/etc/xray/config.json 中 + // 直接访问底层字节切片避免拷贝 + data := result.Bytes() + + // 快速验证JSON格式 + if !json.Valid(data) { + return false, []string{fmt.Sprintf("Invalid JSON format: %s", data)} + } + + // 使用适当权限创建文件并写入(O_WRONLY|O_CREATE|O_TRUNC, 0644) + filename := "/usr/local/etc/xray/config.json" + if err := os.WriteFile(filename, data, 0644); err != nil { + return false, []string{err.Error()} + } + + fmt.Println("xray config file is written to ", filename) + // 移除encode自动添加的换行,重新缩进格式化 + var prettyJSON bytes.Buffer + if err := json.Indent(&prettyJSON, bytes.TrimSpace(data), "", " "); err != nil { + fmt.Printf("JSON indent error: %v\n", err) + } + + fmt.Println(prettyJSON.String()) + fmt.Println() + + // 重启Xray + op.SystemdRestart("xray") + ok, resultLog = op.SystemIsRunning("xray") + if !ok { + log.Error("Xray service is not running => %s Maybe the config file is not valid", resultLog) + return false, resultLog + } + + result.Reset() // 清空全局buffer以备后续使用 + + // 输出 client的配置 + configCache := config.ConfigCache + + // 获取服务器节点名称 + serverNodeName := configCache.Agent.OS.Hostname + + vmessClientConfig := VmessClientConfig{ + ServerNodeName: serverNodeName, + ServerNodeAddress: configCache.Agent.Network.Public.IPv4, + VmessConfig: vmessConfig, + } + + // 执行模板渲染 + clientTmpl, err := template.New("vmessClientConfig").Parse(xray.VmessClientTemplate) + if err != nil { + return false, []string{err.Error()} + } + if err := clientTmpl.Execute(&result, vmessClientConfig); err != nil { + return false, []string{ + err.Error(), + } + } + + // 将result 打印的终端 + fmt.Println("vmess client config is below: ") + fmt.Println(string(result.Bytes())) + fmt.Println() + + return true, []string{"Xray Configuration file written successfully"} +} + +// 安装Xray 最新版本 主机可以联网才可以 +func installXray() { + + installScriptUrl := "https://gitea.107421.xyz/zeaslity/Xray-install/raw/branch/main/install-release.sh" + // 下载安装脚本 + ok, err := utils.DownloadFile(installScriptUrl, "/tmp/install-release.sh") + if !ok { + log.Error("Download Xray install script failed ! from %s error is %s", installScriptUrl, err) + return + } + + // 添加执行权限 + os.Chmod("/tmp/install-release.sh", 0777) + + // 执行安装脚本 + op.SingleLineCommandExecutor([]string{ + "/bin/bash", + "/tmp/install-release.sh", + "-u root", + "install", + }) + + op.SystemdUp("xray") + op.SystemdEnable("xray") + + // 默认服务已经启动! 检查 + ok, resultLog := op.SystemIsRunning("xray") + if !ok { + log.Error("Xray service is not running => %s", resultLog) + return + } + + // 安装成功 + log.Info("Xray installed successfully") + +} diff --git a/agent-wdd/cmd/root.go b/agent-wdd/cmd/root.go index a735674..f8a58d9 100644 --- a/agent-wdd/cmd/root.go +++ b/agent-wdd/cmd/root.go @@ -75,7 +75,7 @@ func Execute() { // 7. info命令 infoCmd := &cobra.Command{ Use: "info", - Short: "打印主机详细信息", + Short: "主机信息", } addInfoSubcommands(infoCmd) diff --git a/agent-wdd/cmd/xray/vmessTemplate.go b/agent-wdd/cmd/xray/vmessTemplate.go new file mode 100644 index 0000000..47769ca --- /dev/null +++ b/agent-wdd/cmd/xray/vmessTemplate.go @@ -0,0 +1,38 @@ +package xray + +var VmessServerTemplate = ` +{ + "log": { + "loglevel": "warning" + }, + "inbounds": [ + { + "listen": "0.0.0.0", + "port": {{.PORT}}, + "protocol": "vmess", + "settings": { + "clients": [ + { + "id": "{{.UUID}}" + } + ] + }, + "streamSettings": { + "network": "tcp" + } + } + ], + "outbounds": [ + { + "protocol": "freedom", + "tag": "direct" + } + ] +} +` + +var VmessClientTemplate = ` +{"type":"vmess","name":"{{.ServerNodeName}}","server":"{{.ServerNodeAddress}}","port":{{.PORT}},"uuid":"{{.UUID}}","alterId":0,"cipher":"auto","network":"tcp"} + +vmess://{"v":"2","ps":"{{.ServerNodeName}}","add":"{{.ServerNodeAddress}}","port":{{.PORT}},"id":"{{.UUID}}","aid":0,"scy":"auto","net":"tcp"} +` diff --git a/agent-wdd/config/Config.go b/agent-wdd/config/Config.go index 7b2a94c..6892795 100644 --- a/agent-wdd/config/Config.go +++ b/agent-wdd/config/Config.go @@ -4,7 +4,9 @@ import ( "agent-wdd/log" "agent-wdd/utils" "os" + "os/exec" "runtime" + "strings" "github.com/spf13/viper" "gopkg.in/yaml.v3" @@ -154,3 +156,44 @@ func SaveConfig() { log.Error("写入文件失败: %w", err) } } + +// 归一化配置-重命名主机名 +func (c *Config) NormalizeConfig() { + // 重命名主机名 + + log.Info("归一化主机配置") + + // 重新读取配置 + InitConfig() + + // 主机名称应该为 City(格式为首字母大写)-amd64-公网IPv4(如果公网IPv4为空,则使用内网IPv4; ip的格式为127-0-0-1) + + // 获取城市(格式为首字母大写) + city := strings.Title(ConfigCache.Agent.Network.Public.City) + + // 获取公网IPv4 ip的格式为127-0-0-1 + ipInfo := ConfigCache.Agent.Network.Public.IPv4 + if ipInfo == "" { + ipInfo = ConfigCache.Agent.Network.Interfaces[0].IPv4 + } + + ipInfo = strings.ReplaceAll(ipInfo, ".", "-") + + // 获取架构 + arch := ConfigCache.Agent.CPU.Arch + + uniformHostname := city + "-" + arch + "-" + ipInfo + + // 重命名主机名 + log.Info("重命名主机名: %s", uniformHostname) + cmd := exec.Command("hostnamectl", "set-hostname", uniformHostname) + + // 执行命令 + cmd.Run() + + // 更新配置 + ConfigCache.Agent.OS.Hostname = uniformHostname + + // 更新配置 + SaveConfig() +} diff --git a/agent-wdd/test/one-click-build-run.ps1 b/agent-wdd/test/one-click-build-run.ps1 index 005be73..fb001c6 100644 --- a/agent-wdd/test/one-click-build-run.ps1 +++ b/agent-wdd/test/one-click-build-run.ps1 @@ -1,27 +1,38 @@ +set HTTP_PROXY=http://127.0.0.1:7899 +set HTTPS_PROXY=http://127.0.0.1:7899 +# 设置 # mc.exe alias set oracle-osaka-1 https://axzsapxffbbn.compat.objectstorage.ap-osaka-1.oraclecloud.com b0696c316f87b644b4a13bcb020f095cd147be0b GAIaM/064epLzQcXsRbj2gwlFOrVepjCR23wj2tfJ+A= +# mc.exe ls oracle-osaka-1/osaka + +# 设置 韩国oss +# mc.exe alias set oracle-seoul-2 https://cncvl8ro2rbf.compat.objectstorage.ap-seoul-1.oraclecloud.com 9e413c6e66269bc65d7ec951d93ba9c6a9781f6e dkXD7PysjrhsTKfNIbKupUmtxdfOvYCyLXf0MXa4hnU= +# mc.exe ls oracle-seoul-2/seoul-2 + # 重新build项目 Set-Location "C:\Users\wddsh\Documents\IdeaProjects\ProjectOctopus\agent-wdd" & "C:\Users\wddsh\go\bin\gox.exe" -osarch="linux/amd64" -output "build/agent-wdd_{{.OS}}_{{.Arch}}" - - -# mc.exe ls oracle-osaka-1/osaka -# 删除上面存在的旧的内容 -mc.exe rm oracle-osaka-1/osaka/agent-wdd_linux_amd64 -mc.exe rm oracle-osaka-1/osaka/test-shell.sh - - -# 上传文件 -mc.exe cp C:\Users\wddsh\Documents\IdeaProjects\ProjectOctopus\agent-wdd\build\agent-wdd_linux_amd64 oracle-osaka-1/osaka/ -mc.exe cp C:\Users\wddsh\Documents\IdeaProjects\ProjectOctopus\agent-wdd\test\test-shell.sh oracle-osaka-1/osaka/ - - Set-Location "C:\Users\wddsh\Documents\IdeaProjects\ProjectOctopus\agent-wdd\test" +# 删除上面存在的旧的内容 +mc.exe rm oracle-seoul-2/seoul-2/agent-wdd_linux_amd64 +mc.exe rm oracle-seoul-2/seoul-2/test-shell.sh + + +# 上传文件 +mc.exe cp C:\Users\wddsh\Documents\IdeaProjects\ProjectOctopus\agent-wdd\build\agent-wdd_linux_amd64 oracle-seoul-2/seoul-2/ +mc.exe cp C:\Users\wddsh\Documents\IdeaProjects\ProjectOctopus\agent-wdd\test\test-shell.sh oracle-seoul-2/seoul-2/ + + + + + + + diff --git a/agent-wdd/test/run_test.sh b/agent-wdd/test/run_test.sh index 6eeaafe..be780fd 100644 --- a/agent-wdd/test/run_test.sh +++ b/agent-wdd/test/run_test.sh @@ -3,15 +3,17 @@ rm -f /usr/local/bin/agent-wdd rm -f /usr/local/bin/test-shell.sh -wget https://pan.107421.xyz/d/oracle-osaka-1/agent-wdd_linux_amd64 -O /usr/local/bin/agent-wdd +wget https://pan.107421.xyz/d/oracle-seoul-2/agent-wdd_linux_amd64 -O /usr/local/bin/agent-wdd chmod +x /usr/local/bin/agent-wdd -wget https://pan.107421.xyz/d/oracle-osaka-1/test-shell.sh -O /usr/local/bin/test-shell.sh +wget https://pan.107421.xyz/d/oracle-seoul-2/test-shell.sh -O /usr/local/bin/test-shell.sh chmod +x /usr/local/bin/test-shell.sh +/usr/local/bin/agent-wdd info all +cat /usr/local/etc/wdd/agent-wdd-config.yaml @@ -26,7 +28,8 @@ bash /usr/local/bin/test-shell.sh /usr/local/bin/agent-wdd info swap /usr/local/bin/agent-wdd info disks -cat /usr/local/etc/wdd/agent-wdd-config.yaml + + /usr/local/bin/agent-wdd base docker local @@ -50,3 +53,8 @@ cat /usr/local/etc/wdd/agent-wdd-config.yaml /usr/local/bin/agent-wdd download https://pan.107421.xyz/d/oracle-osaka-1/docker-amd64-20.10.15.tgz /root/wdd +/usr/local/bin/agent-wdd proxy install + +/usr/local/bin/agent-wdd proxy vmess 22443 + + diff --git a/agent-wdd/utils/PrintUtils.go b/agent-wdd/utils/PrintUtils.go index b6f845f..a731f54 100644 --- a/agent-wdd/utils/PrintUtils.go +++ b/agent-wdd/utils/PrintUtils.go @@ -32,10 +32,10 @@ func BeautifulPrintToString(object interface{}) string { func BeautifulPrintWithTitle(contend any, title string) { fmt.Println() - fmt.Println(fmt.Sprintf("content tile is => %s", title)) + fmt.Println(">>>>>>>> " + title + " <<<<<<<<") bytes, _ := json.MarshalIndent(contend, "", " ") fmt.Println(string(bytes)) - fmt.Println("---------- end -----------") + fmt.Println(">>>>>>>> end <<<<<<<<") } func BeautifulPrintListWithTitle(contend []string, title string) { @@ -45,7 +45,7 @@ func BeautifulPrintListWithTitle(contend []string, title string) { for _, line := range contend { fmt.Println(line) } - fmt.Println("---------- end -----------") + fmt.Println(">>>>>>>> end <<<<<<<<") } func SplitLinePrint() {