golang远程执行Windows机器命令的插件库winrm-cli的使用

winrm-cli

这是一个通过WinRM/WinRS在Windows机器上执行远程命令的Go命令行工具。

注意:此工具不支持域用户(不支持GSSAPI或Kerberos)。它主要用于在EC2 Windows机器上执行远程命令。

准备工作

为基本认证准备远程Windows机器

此项目仅支持本地账户的基本认证(不支持域用户)。远程Windows系统必须准备好winrm:

在远程主机上,以管理员身份运行PowerShell,并粘贴以下命令:

winrm quickconfig
y
winrm set winrm/config/service/Auth '@{Basic="true"}'
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="1024"}'

注意

  • Windows防火墙需要运行才能执行此命令
  • 不要禁用Negotiate认证,因为Windows winrm命令本身使用此进行内部认证
  • 某些Windows 2008R2系统上MaxMemoryPerShellMB选项无效,这是WinRM的一个bug

构建winrm-cli

你可以从源代码构建winrm-cli:

git clone https://github.com/masterzen/winrm-cli
cd winrm-cli
make

这将生成一个名为./winrm的二进制文件。

注意:需要Go 1.5+版本。可以通过以下命令检查你的安装:

go version

命令行使用

构建完成后,你可以像这样运行远程命令:

./winrm -hostname remote.domain.com -username "Administrator" -password "secret" "ipconfig /all"

Docker使用

构建镜像

docker build -t winrm .

使用

构建完成后,你可以像这样运行远程命令:

docker run -it --rm winrm -hostname remote.domain.com -username "Administrator" -password "secret" "ipconfig /all"

完整示例代码

以下是一个完整的Go代码示例,展示如何使用winrm-cli库:

package main

import (
	"fmt"
	"os"
	"os/exec"
)

func main() {
	// 设置远程主机信息
	hostname := "remote.domain.com"
	username := "Administrator"
	password := "secret"
	command := "ipconfig /all"

	// 构建winrm命令
	cmd := exec.Command("./winrm", 
		"-hostname", hostname,
		"-username", username,
		"-password", password,
		command)

	// 设置输出
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	// 执行命令
	err := cmd.Run()
	if err != nil {
		fmt.Printf("命令执行失败: %v\n", err)
		os.Exit(1)
	}
}

注意事项

  1. 确保远程Windows机器已正确配置WinRM服务
  2. 使用基本认证时传输未加密,建议在安全网络环境中使用
  3. 对于生产环境,建议使用更安全的认证方式
  4. 执行内存密集型命令时可能需要调整MaxMemoryPerShellMB

更多关于golang远程执行Windows机器命令的插件库winrm-cli的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang远程执行Windows机器命令的插件库winrm-cli的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用winrm-cli在Golang中远程执行Windows命令

winrm-cli是一个Golang库,允许通过Windows远程管理(WinRM)协议在远程Windows机器上执行命令。下面我将详细介绍如何使用这个库。

安装winrm-cli

首先需要安装winrm-cli库:

go get github.com/masterzen/winrm-cli

基本用法

1. 创建WinRM客户端

package main

import (
	"fmt"
	"os"
	
	"github.com/masterzen/winrm"
)

func main() {
	// 配置WinRM客户端参数
	endpoint := "https://192.168.1.100:5986/wsman" // WinRM端点
	username := "administrator"                      // 管理员用户名
	password := "yourpassword"                      // 密码
	
	// 创建WinRM客户端
	client, err := winrm.NewClient(
		winrm.NewEndpoint(
			endpoint, // 端点URL
			5986,     // 端口
			false,    // 是否使用HTTPS
			false,    // 不验证TLS证书
			nil,      // CA证书
			nil,      // 客户端证书
			nil,      // 客户端私钥
			0,        // 超时时间(0表示默认)
		),
		username,
		password,
	)
	if err != nil {
		fmt.Printf("创建WinRM客户端失败: %v\n", err)
		os.Exit(1)
	}
	
	// 测试连接
	_, err = client.Run("echo Hello, WinRM!", os.Stdout, os.Stderr)
	if err != nil {
		fmt.Printf("连接测试失败: %v\n", err)
		os.Exit(1)
	}
	
	fmt.Println("连接成功!")
}

2. 执行远程命令

func executeRemoteCommand(client *winrm.Client, command string) (string, error) {
	var stdout, stderr bytes.Buffer
	exitCode, err := client.Run(command, &stdout, &stderr)
	
	if err != nil {
		return "", fmt.Errorf("执行命令失败: %v, 退出码: %d, 错误输出: %s", 
			err, exitCode, stderr.String())
	}
	
	if stderr.Len() > 0 {
		return stdout.String(), fmt.Errorf("命令执行有错误输出: %s", stderr.String())
	}
	
	return stdout.String(), nil
}

// 使用示例
output, err := executeRemoteCommand(client, "ipconfig")
if err != nil {
    fmt.Printf("错误: %v\n", err)
} else {
    fmt.Printf("命令输出:\n%s\n", output)
}

3. 执行PowerShell脚本

func executePowerShell(client *winrm.Client, script string) (string, error) {
	// 将脚本转换为Base64编码,避免特殊字符问题
	encodedScript := base64.StdEncoding.EncodeToString([]byte(script))
	command := fmt.Sprintf("powershell -encodedcommand %s", encodedScript)
	
	return executeRemoteCommand(client, command)
}

// 使用示例
psScript := `Get-Process | Where-Object { $_.CPU -gt 100 } | Select-Object Name, CPU`
output, err := executePowerShell(client, psScript)
if err != nil {
    fmt.Printf("执行PowerShell失败: %v\n", err)
} else {
    fmt.Printf("PowerShell输出:\n%s\n", output)
}

高级功能

1. 文件传输

func uploadFile(client *winrm.Client, localPath, remotePath string) error {
	content, err := os.ReadFile(localPath)
	if err != nil {
		return fmt.Errorf("读取本地文件失败: %v", err)
	}
	
	// 将文件内容转换为Base64
	encodedContent := base64.StdEncoding.EncodeToString(content)
	
	// 创建PowerShell脚本解码并保存文件
	script := fmt.Sprintf(`
		$content = "%s"
		$bytes = [Convert]::FromBase64String($content)
		[IO.File]::WriteAllBytes("%s", $bytes)
	`, encodedContent, remotePath)
	
	_, err = executePowerShell(client, script)
	return err
}

// 使用示例
err := uploadFile(client, "localfile.txt", "C:\\temp\\remotefile.txt")
if err != nil {
    fmt.Printf("文件上传失败: %v\n", err)
}

2. 长时间运行命令

func runLongCommand(client *winrm.Client, command string) error {
	shell, err := client.CreateShell()
	if err != nil {
		return fmt.Errorf("创建Shell失败: %v", err)
	}
	defer shell.Close()
	
	cmd, err := shell.Execute(command)
	if err != nil {
		return fmt.Errorf("执行命令失败: %v", err)
	}
	defer cmd.Close()
	
	var stdout, stderr bytes.Buffer
	go io.Copy(&stdout, cmd.Stdout)
	go io.Copy(&stderr, cmd.Stderr)
	
	cmd.Wait()
	
	if stderr.Len() > 0 {
		return fmt.Errorf("命令执行有错误输出: %s", stderr.String())
	}
	
	fmt.Printf("命令输出:\n%s\n", stdout.String())
	return nil
}

安全注意事项

  1. 使用HTTPS:在生产环境中始终使用HTTPS而不是HTTP
  2. 证书验证:应该验证服务器证书,不要跳过验证
  3. 凭据安全:不要将凭据硬编码在代码中,使用环境变量或配置管理工具
  4. 最小权限:使用具有最小必要权限的账户

完整示例

package main

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"os"
	
	"github.com/masterzen/winrm"
)

func main() {
	// 从环境变量获取凭据
	endpoint := os.Getenv("WINRM_ENDPOINT")
	username := os.Getenv("WINRM_USERNAME")
	password := os.Getenv("WINRM_PASSWORD")
	
	if endpoint == "" || username == "" || password == "" {
		fmt.Println("请设置WINRM_ENDPOINT, WINRM_USERNAME和WINRM_PASSWORD环境变量")
		os.Exit(1)
	}
	
	// 创建WinRM客户端
	client, err := winrm.NewClient(
		winrm.NewEndpoint(
			endpoint,
			5986,
			true,  // 使用HTTPS
			false, // 不验证证书(生产环境应该为true)
			nil, nil, nil, 0),
		username,
		password,
	)
	if err != nil {
		fmt.Printf("创建WinRM客户端失败: %v\n", err)
		os.Exit(1)
	}
	
	// 执行系统信息命令
	output, err := executeRemoteCommand(client, "systeminfo")
	if err != nil {
		fmt.Printf("获取系统信息失败: %v\n", err)
	} else {
		fmt.Printf("系统信息:\n%s\n", output)
	}
	
	// 执行PowerShell命令
	psOutput, err := executePowerShell(client, "Get-Service | Where-Object { $_.Status -eq 'Running' }")
	if err != nil {
		fmt.Printf("执行PowerShell失败: %v\n", err)
	} else {
		fmt.Printf("运行中的服务:\n%s\n", psOutput)
	}
}

// executeRemoteCommand 和 executePowerShell 函数同上文示例

winrm-cli库提供了强大的功能来远程管理Windows服务器。通过合理使用,可以构建自动化运维工具、部署脚本等。记得始终遵循安全最佳实践来保护你的远程管理连接。

回到顶部