编程 WinRM for Go — 用Go语言远程管理Windows机器

2026-07-04 07:36:57 +0800 CST views 10

WinRM for Go — 用 Go 语言远程管理 Windows 机器

WinRM for Go 是一个纯 Go 语言编写的库,让你能通过 WinRM/WinRS 协议在 Windows 机器上执行远程命令。它支持本地账户的基本认证、NTLM 认证以及 Kerberos 域用户认证,甚至可以通过 SSH 隧道穿透网络。本文从环境准备到代码实战,带你一步步上手这个工具。


01 为什么需要 WinRM for Go

日常运维中经常要远程操作 Windows 服务器,尤其是云上的 EC2 实例。传统做法是开 RDP 进去手动执行命令,但在自动化场景下显然不现实。PowerShell Remoting 是个方案,可如果你是 Go 开发者,想直接把远程执行命令集成到自己的工具或服务里,就得找个好用的库。

WinRM for Go 就是干这个的。 它用 Go 实现了 WinRM(Windows Remote Management)协议,让你在代码里就能对 Windows 机器执行 shell 命令、获取输出,就像 SSH 连 Linux 一样自然


02 核心功能

功能说明
基本认证支持本地账户
NTLM 认证比如连 Azure 虚拟机
Kerberos 认证域用户环境
HTTPS + 客户端证书更安全的认证方式
自定义 dialer例如通过 SSH 隧道中转
纯 Go 实现不依赖 CGO 或外部库

03 环境准备:Windows 端配置

3.1 基本认证(本地账户)

要在远程 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 认证,因为 WinRM 自身依赖它
  • 如果你的 Windows 2008R2 有内存限制的 bug,需要打 KB2842230 补丁

3.2 Kerberos 认证(域用户)

如果你需要用域账号连接,配置稍有不同:

winrm quickconfig
# 输入 y 确认

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

注意:不需要设置 Basic=true,因为 Kerberos 本身加密。


04 安装与构建

确保你的 Go 版本不低于 1.5(新版建议 1.7+)。克隆项目后直接编译:

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

如果你只是想作为库引用,直接 go get 即可:

go get github.com/masterzen/winrm

05 代码实战:四种典型用法

5.1 最快上手:HTTP 明文

package main

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

func main() {
    endpoint := winrm.NewEndpoint(host, 5986, false, false, nil, nil, nil, 0)
    client, err := winrm.NewClient(endpoint, "Administrator", "secret")
    if err != nil {
        panic(err)
    }
    
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    
    client.RunWithContext(ctx, "ipconfig /all", os.Stdout, os.Stderr)
}

endpoint 参数说明

winrm.NewEndpoint(
    host,           // 主机地址
    5986,           // 端口(HTTP 5985,HTTPS 5986)
    false,          // 是否 HTTPS
    false,          // 是否跳过证书验证
    nil,            // CA 证书
    nil,            // 客户端证书
    nil,            // 客户端密钥
    0,              // 超时(0 表示不限制)
)

5.2 支持输入:交互式命令

package main

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

func main() {
    endpoint := winrm.NewEndpoint("localhost", 5985, false, false, nil, nil, nil, 0)
    client, err := winrm.NewClient(endpoint, "Administrator", "secret")
    if err != nil {
        panic(err)
    }
    
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    
    _, err = client.RunWithContextWithInput(ctx, "ipconfig", os.Stdout, os.Stderr, os.Stdin)
    if err != nil {
        panic(err)
    }
}

差别:第三个参数传入了 stdin,可以让命令交互。

5.3 NTLM 认证(连接 Azure VM)

package main

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

func main() {
    endpoint := winrm.NewEndpoint("localhost", 5985, false, false, nil, nil, nil, 0)
    
    params := winrm.DefaultParameters
    params.TransportDecorator = func() winrm.Transporter { 
        return &winrm.ClientNTLM{} 
    }
    
    client, err := winrm.NewClientWithParameters(endpoint, "test", "test", params)
    if err != nil {
        panic(err)
    }
    
    _, err = client.RunWithInput("ipconfig", os.Stdout, os.Stderr, os.Stdin)
    if err != nil {
        panic(err)
    }
}

核心:自定义 TransportDecorator,换成 NTLM 认证器。

5.4 通过 SSH 隧道连接内网机器

package main

import (
    "github.com/masterzen/winrm"
    "golang.org/x/crypto/ssh"
    "os"
    "context"
)

func main() {
    // 1. 先建立 SSH 隧道
    sshClient, err := ssh.Dial("tcp", "localhost:22", &ssh.ClientConfig{
        User: "ubuntu",
        Auth: []ssh.AuthMethod{ssh.Password("ubuntu")},
        HostKeyCallback: ssh.InsecureIgnoreHostKey(),
    })
    if err != nil {
        panic(err)
    }
    
    // 2. 通过 SSH 隧道连接 WinRM
    endpoint := winrm.NewEndpoint("other-host", 5985, false, false, nil, nil, nil, 0)
    params := winrm.DefaultParameters
    params.Dial = sshClient.Dial  // 关键:使用 SSH 隧道的 Dial
    
    client, err := winrm.NewClientWithParameters(endpoint, "test", "test", params)
    if err != nil {
        panic(err)
    }
    
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    
    client.RunWithContext(ctx, "ipconfig", os.Stdout, os.Stderr)
}

场景:Windows 机器在内网,无法直接访问,需要通过跳板机(SSH)中转。


06 常见问题

Q1: WinRM 端口是多少?

  • HTTP: 5985
  • HTTPS: 5986

Q2: 如何启用 HTTPS?

# 创建自签名证书
$cert = New-SelfSignedCertificate -DnsName "your-server" -CertStoreLocation "Cert:\LocalMachine\My"

# 配置 WinRM 使用 HTTPS
winrm create winrm/config/Listener?Address=*+Transport=HTTPS '@{Hostname="your-server";CertificateThumbprint="$($cert.Thumbprint)"}'

Q3: 如何调试连接问题?

# 查看 WinRM 配置
winrm get winrm/config

# 测试连接
winrm identify -remote:localhost

07 对比其他方案

方案语言优点缺点
WinRM for GoGo纯 Go,易集成需要配置 WinRM
PowerShell RemotingPowerShell原生支持仅限 PowerShell 脚本
WMI任意广泛支持功能受限
RDPGUI可视化无法自动化

08 相关链接


总结

WinRM for Go 让 Go 开发者能够像 SSH 连 Linux 一样远程操作 Windows 服务器:

  • 纯 Go 实现:无 CGO 依赖,跨平台编译
  • 多种认证:Basic、NTLM、Kerberos、HTTPS 客户端证书
  • SSH 隧道:穿透内网,连接隔离环境的 Windows 机器
  • 交互式命令:支持 stdin 输入,适合交互式脚本

如果你需要在 Go 项目中远程管理 Windows 服务器,WinRM for Go 是不二之选


原文来自微信公众号。

推荐文章

前端如何优化资源加载
2024-11-18 13:35:45 +0800 CST
mysql 计算附近的人
2024-11-18 13:51:11 +0800 CST
程序员茄子在线接单