winrm

package module
v0.0.0-...-a8065f4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 10, 2026 License: Apache-2.0 Imports: 39 Imported by: 0

README

WinRM for Go

Fork of github.com/masterzen/winrm by Brice Figureau and contributors. This fork extends the original with Message Level Encryption (NTLM + Kerberos), the PowerShell Remoting Protocol layer, file transfer, structured WS-Man fault parsing and retry handling. All original code remains under its existing licence; thanks to the upstream maintainers for the foundation this builds on.

Note: if you're looking for the winrm command-line tool, that lives at the upstream winrm-cli project.

A Go library to execute remote commands on Windows machines through WinRM/WinRS.

What this fork adds

  • Message Level Encryption (MLE) for NTLM (ClientNTLMEncrypted) and Kerberos (ClientKerberosEncrypted) transports — required for HTTP endpoints with AllowUnencrypted=false.
  • PowerShell Remoting Protocol (PSRP) — message framing, fragmentation, CLIXML serialization, runspace-pool / pipeline state machine and WS-Man PSRP plugin SOAP wiring (psrp/ + request_psrp.go). Streaming callback for live output.
  • File transfer — chunked upload/download via PowerShell (file.FileManager).
  • Structured WS-Man faults*WSManFaultError (errors.As-friendly).
  • Transient retryParameters.RetryAttempts / RetryDelay, exponential backoff, faults short-circuit.
  • Context everywhereTransporter.Post takes context.Context; cancellation propagates to the HTTP call.

Contact

Requirements

  • Go 1.21+

Auth matrix

Scenario Transport Notes
HTTPS + Basic default Channel encryption from TLS.
HTTPS + NTLM (auth only) ClientNTLM TLS handles encryption.
HTTP + NTLM with MLE ClientNTLMEncrypted For default WinRM (AllowUnencrypted=false).
HTTPS + Kerberos (auth only) ClientKerberos TLS handles encryption.
HTTP + Kerberos / SPNEGO with MLE ClientKerberosEncrypted Domain-joined hosts.
HTTPS + x509 client cert ClientAuthRequest Mutual TLS.

The legacy Encryption decorator (in transport_legacy_mle.go) remains for compatibility but new code should use ClientNTLMEncrypted / ClientKerberosEncrypted.

Preparing the remote Windows host

Basic auth (local accounts only; domain users not supported by Basic)

Run a PowerShell prompt as Administrator:

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"}'

For a one-shot script see Richard Downer's blog.

N.B.: Windows Firewall must be running to accept this command — see KB#2004640.

N.B.: Do not disable Negotiate authentication; the winrm command itself uses it for internal authentication.

N.B.: MaxMemoryPerShellMB has no effect on some Windows 2008R2 systems due to a WinRM bug — install the hotfix in KB#2842230 for shells using over 150 MB.

For more on WinRM, see the Microsoft docs.

Kerberos (domain users)
winrm quickconfig
y
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="1024"}'

All N.B. points from the Basic section also apply.

If AllowUnencrypted is left at its default false value, use ClientNTLMEncrypted or ClientKerberosEncrypted for HTTP; no host-side change is needed.

Building from source

git clone https://codeberg.org/sholt0r/winrm
cd winrm
make           # build everything
make help      # list all make targets

Command-line usage

For command-line usage check the winrm-cli project.

Library usage

The API is subject to change.

A short, fast invocation (no stdin):

package main

import (
    "context"
    "os"

    "codeberg.org/sholt0r/winrm"
)

func main() {
    endpoint := winrm.NewEndpoint("host", 5986, true, 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)
}

With stdin:

package main

import (
    "context"
    "os"

    "codeberg.org/sholt0r/winrm"
)

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()
    if _, err := client.RunWithContextWithInput(ctx, "ipconfig", os.Stdout, os.Stderr, os.Stdin); err != nil {
        panic(err)
    }
}
NTLM (auth only)

For HTTPS endpoints, where TLS handles channel encryption:

endpoint := winrm.NewEndpoint("localhost", 5986, true, 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)
}

if _, err := client.RunWithInput("ipconfig", os.Stdout, os.Stderr, os.Stdin); err != nil {
    panic(err)
}
Kerberos (auth only)
endpoint := winrm.NewEndpoint("srv-win", 5986, true, false, nil, nil, nil, 0)

params := winrm.DefaultParameters
params.TransportDecorator = func() winrm.Transporter {
    return &winrm.ClientKerberos{
        Username: "test",
        Password: "s3cr3t",
        Hostname: "srv-win",
        Realm:    "DOMAIN.LAN",
        Port:     5986,
        Proto:    "https",
        KrbConf:  "/etc/krb5.conf",
        SPN:      "HTTP/srv-win",
    }
}

client, err := winrm.NewClientWithParameters(endpoint, "test", "s3cr3t", params)
if err != nil {
    panic(err)
}

ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if _, err := client.RunWithContextWithInput(ctx, "ipconfig", os.Stdout, os.Stderr, os.Stdin); err != nil {
    panic(err)
}
Custom dialer (e.g. SSH tunnel)
package main

import (
    "context"
    "os"

    "codeberg.org/sholt0r/winrm"
    "golang.org/x/crypto/ssh"
)

func main() {
    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)
    }

    endpoint := winrm.NewEndpoint("other-host", 5985, false, false, nil, nil, nil, 0)

    params := winrm.DefaultParameters
    params.Dial = sshClient.Dial

    client, err := winrm.NewClientWithParameters(endpoint, "test", "test", params)
    if err != nil {
        panic(err)
    }

    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    if _, err := client.RunWithContextWithInput(ctx, "ipconfig", os.Stdout, os.Stderr, os.Stdin); err != nil {
        panic(err)
    }
}
Direct shell + command control
package main

import (
    "bytes"
    "context"
    "io"
    "os"

    "codeberg.org/sholt0r/winrm"
)

func main() {
    stdin := bytes.NewBufferString("ipconfig /all")
    endpoint := winrm.NewEndpoint("localhost", 5985, false, false, nil, nil, nil, 0)
    client, err := winrm.NewClient(endpoint, "Administrator", "secret")
    if err != nil {
        panic(err)
    }
    shell, err := client.CreateShell()
    if err != nil {
        panic(err)
    }
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    cmd, err := shell.ExecuteWithContext(ctx, "cmd.exe")
    if err != nil {
        panic(err)
    }

    go io.Copy(cmd.Stdin, stdin)
    go io.Copy(os.Stdout, cmd.Stdout)
    go io.Copy(os.Stderr, cmd.Stderr)

    cmd.Wait()
    shell.Close()
}
HTTPS with x509 client cert (no CA check)
package main

import (
    "context"
    "log"
    "os"

    "codeberg.org/sholt0r/winrm"
)

func main() {
    clientCert, err := os.ReadFile("/home/example/winrm_client_cert.pem")
    if err != nil {
        log.Fatalf("failed to read client certificate: %q", err)
    }

    clientKey, err := os.ReadFile("/home/example/winrm_client_key.pem")
    if err != nil {
        log.Fatalf("failed to read client key: %q", err)
    }

    winrm.DefaultParameters.TransportDecorator = func() winrm.Transporter {
        return &winrm.ClientAuthRequest{}
    }

    endpoint := winrm.NewEndpoint(
        "192.168.100.2", // host
        5986,            // winrm port
        true,            // use TLS
        true,            // allow insecure
        nil,             // CA cert
        clientCert,      // client cert
        clientKey,       // client key
        0,               // timeout
    )
    client, err := winrm.NewClient(endpoint, "Administrator", "")
    if err != nil {
        log.Fatalf("failed to create client: %q", err)
    }
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
    if _, err = client.RunWithContext(ctx, "whoami", os.Stdout, os.Stderr); err != nil {
        log.Fatalf("failed to run command: %q", err)
    }
}

Cancelling the context.Context passed to API methods does not cancel the in-flight HTTP request directly — instead, a running command is aborted on the remote host via command.Stop().

Extended features

Message Level Encryption (MLE)

For HTTP endpoints (port 5985) where WinRM has AllowUnencrypted=false, use the encrypted variants of the auth transports. Encryption follows pywinrm winrm/encryption.py semantics.

NTLM with MLE:

params := winrm.DefaultParameters
params.TransportDecorator = func() winrm.Transporter {
    return &winrm.ClientNTLMEncrypted{}
}
client, err := winrm.NewClientWithParameters(endpoint, user, pass, params)

Constructor variants for custom dialer or proxy:

winrm.NewClientNTLMEncryptedWithDial(myDial)
winrm.NewClientNTLMEncryptedWithProxyFunc(myProxyFunc)

Kerberos with MLE:

kt := winrm.NewClientKerberosEncrypted(&winrm.Settings{
    WinRMUsername: "alice",
    WinRMPassword: "pw",
    WinRMHost:     "host.example",
    WinRMPort:     5985,
    WinRMProto:    "http",
    KrbRealm:      "EXAMPLE.COM",
    KrbConfig:     "/etc/krb5.conf",
    KrbSpn:        "HTTP/host.example",
})

params := winrm.DefaultParameters
params.TransportDecorator = func() winrm.Transporter { return kt }

client, err := winrm.NewClientWithParameters(endpoint, "alice", "pw", params)

NewClientKerberosEncryptedWithDial and NewClientKerberosEncryptedWithProxyFunc are also available.

For HTTPS endpoints, prefer ClientNTLM / ClientKerberos (auth-only; TLS handles channel encryption).

To implement custom encryption, satisfy the encryption.Sealer interface:

type Sealer interface {
    Seal(plaintext []byte) ([]byte, error)
    Unseal(sealed []byte) ([]byte, error)
    ProtocolHeader() string
}
PowerShell Remoting (PSRP)

The psrp/ package implements MS-PSRP message framing, fragmentation, CLIXML serialization and the runspace-pool / pipeline state machine. The WS-Man PSRP plugin SOAP request builders (PSRP ResourceURI, <creationXml>, stdin pr / stdout streams) live in request_psrp.go and response_psrp.go. The high-level surface lives on *Client:

ps, err := client.CreatePowerShell()
if err != nil { ... }
defer ps.Close()

out, err := ps.Execute("Get-Process | Select-Object -First 5")
for _, obj := range out.Objects {
    s, _ := obj.Str()
    fmt.Println(s)
}
if out.HadErrors {
    for _, e := range out.Errors {
        fmt.Println("error:", e)
    }
}

CreatePowerShellWithContext(ctx) propagates a context through the WS-Man HTTP requests; CreateRunspacePool exposes the lower-level psrp.RunspacePool for callers that want to drive pipelines directly.

After the WS-Man Create succeeds, CreatePowerShell drains pool-scoped Receive batches until the server emits a RUNSPACEPOOL_STATE = Opened message so the first Execute does not race the pool initialisation.

Streaming output

PowerShell.ExecuteStreaming(script, cb) invokes cb(stream, value) for every value dispatched as it arrives, while still returning the accumulated output:

out, err := ps.ExecuteStreaming(`1..5 | ForEach-Object { Start-Sleep -Seconds 1; "tick $_" }`,
    func(stream string, v *psrp.Value) {
        s, _ := v.Str()
        fmt.Printf("[%s] %s\n", stream, s)
    })

stream is one of output, error, warning, verbose, debug, information.

File transfer

Chunked base64 file transfer over PowerShell, modeled after winrm-fs:

fm := client.NewFileManager()

err := fm.Upload(ctx, "/local/x.bin", `C:\dest\x.bin`, func(sent, total int64) {
    fmt.Printf("upload: %d/%d\n", sent, total)
})

err = fm.Download(ctx, `C:\src.bin`, "/local/got.bin", nil)

Performance is bounded by the chunk-per-PowerShell-invocation model. The default ChunkSize (1024 bytes) is sized to fit cmd.exe's ~8 KiB command-line cap after the encoded-PowerShell wrapper, UTF-16 re-encoding and outer base64 expansion. For files larger than ~100 MB consider an alternative transfer channel; for moderate transfers expect roughly one round-trip per kilobyte. Once the PSRP transport pathway is wired, fm.ChunkSize can safely be raised because PSRP has a much larger envelope.

Structured WS-Man faults

SOAP fault responses are parsed into *WSManFaultError for structured access:

var fe *winrm.WSManFaultError
if errors.As(err, &fe) {
    fmt.Println(fe.Code, fe.Subcode, fe.WSManFaultCode, fe.Reason)
}

winrm.IsWSManFault(err) is a shorthand.

Retries

Set RetryAttempts on Parameters to retry transient transport failures (network errors, timeouts, 5xx). Semantic errors (faults, non-zero command exits) are not retried:

params := winrm.DefaultParameters
params.RetryAttempts = 3
params.RetryDelay = 500 * time.Millisecond
client, err := winrm.NewClientWithParameters(endpoint, user, pass, params)

Backoff doubles per attempt and is capped at 30 s.

Migration notes

Transporter.Post now takes a context.Context

The Transporter interface gained a context.Context first argument:

type Transporter interface {
    Post(ctx context.Context, client *Client, msg *soap.SoapMessage) (string, error)
    Transport(*Endpoint) error
}

Custom transports must update their Post signature accordingly. Replace http.NewRequest with http.NewRequestWithContext(ctx, ...) so cancellation and timeouts propagate to the underlying HTTP call. A nil context is acceptable and is treated as context.Background() by the built-in transports.

Context-aware shell helpers

The high-level surface gained Client.CreateShellWithContext(ctx) and Shell.CloseWithContext(ctx). The original no-context variants remain for compatibility but are now deprecated thin wrappers.

PSRP Pipeline output behind accessors

PSRP Pipeline exposes its output streams via accessor methods (Output(), Errors(), Warnings(), Verbose(), Debug(), Information()) which return defensive copies. The previously-public slice fields are unexported.

KerberosSealer fields unexported

encryption.KerberosSealer no longer exposes its key/sequence-number fields directly; use OutgoingSeqNum() / IncomingSeqNum() to introspect.

Developing on WinRM

make help          # list all targets
make check         # vet + unit tests (default pre-push gate)
make test-cover    # coverage profile
make test-race     # with race detector
make lint          # golangci-lint (requires it on $PATH)

Project layout, contribution workflow and the integration-test runner are documented in CONTRIBUTING.md. Live-host integration tests live in test/integration/ gated behind the integration build tag — see test/integration/README.md for the env-var matrix.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var DefaultParameters = NewParameters("PT60S", "en-US", 153600)

DefaultParameters return constant config of type Parameters

Functions

func IsWSManFault

func IsWSManFault(err error) bool

IsWSManFault is a convenience wrapper around errors.As.

func NewClientWithDial

func NewClientWithDial(dial func(network, addr string) (net.Conn, error)) *clientRequest

NewClientWithDial NewClientWithDial

func NewClientWithProxyFunc

func NewClientWithProxyFunc(proxyfunc func(req *http.Request) (*url.URL, error)) *clientRequest

NewClientWithProxyFunc NewClientWithProxyFunc

func NewDeleteShellPSRPRequest

func NewDeleteShellPSRPRequest(uri, shellID string, params *Parameters) *soap.SoapMessage

NewDeleteShellPSRPRequest builds the WS-Man Delete message for a PSRP shell. Mirrors NewDeleteShellRequest but with the PSRP ResourceURI.

func NewDeleteShellRequest

func NewDeleteShellRequest(uri, shellID string, params *Parameters) *soap.SoapMessage

NewDeleteShellRequest ...

func NewExecuteCommandRequest

func NewExecuteCommandRequest(uri, shellID, command string, arguments []string, params *Parameters) *soap.SoapMessage

NewExecuteCommandRequest exec command on specific shellID

func NewExecutePipelinePSRPRequest

func NewExecutePipelinePSRPRequest(uri, shellID, commandID string, firstFragment []byte, params *Parameters) *soap.SoapMessage

NewExecutePipelinePSRPRequest builds the WS-Man Command message that starts a PSRP pipeline. The first fragment of the CREATE_PIPELINE message goes in <rsp:Arguments>; subsequent fragments are shipped via NewSendPSRPRequest with stream "stdin".

Reference: node-winrm src/wsmv/create-pipeline.ts.

func NewGetOutputPSRPRequest

func NewGetOutputPSRPRequest(uri, shellID, commandID string, params *Parameters) *soap.SoapMessage

NewGetOutputPSRPRequest builds a WS-Man Receive message for PSRP output. The DesiredStream is "stdout"; PSRP carries all output streams (errors, warnings, etc.) inside that single WS-Man stream as PSRP message fragments.

Reference: node-winrm src/wsmv/receive.ts (CommandOutputMessage).

func NewGetOutputRequest

func NewGetOutputRequest(uri, shellID, commandID, streams string, params *Parameters) *soap.SoapMessage

NewGetOutputRequest NewGetOutputRequest

func NewOpenShellPSRPRequest

func NewOpenShellPSRPRequest(uri, shellID string, creationXMLPayload []byte, params *Parameters) *soap.SoapMessage

NewOpenShellPSRPRequest builds the WS-Man Create message that opens a PSRP runspace pool. shellID must be a UUID generated by the caller and reused as the runspace pool ID. creationXMLPayload is the concatenated fragment bytes for the SESSION_CAPABILITY + INIT_RUNSPACEPOOL PSRP messages; it is embedded base64-encoded in the <creationXml> element.

Reference: node-winrm src/wsmv/init-runspace-pool.ts (createBody).

func NewOpenShellRequest

func NewOpenShellRequest(uri string, params *Parameters) *soap.SoapMessage

NewOpenShellRequest makes a new soap request

func NewSendInputRequest

func NewSendInputRequest(uri, shellID, commandID string, input []byte, eof bool, params *Parameters) *soap.SoapMessage

NewSendInputRequest NewSendInputRequest

func NewSendPSRPRequest

func NewSendPSRPRequest(uri, shellID, commandID, streamName string, fragmentBytes []byte, params *Parameters) *soap.SoapMessage

NewSendPSRPRequest builds a WS-Man Send message carrying additional PSRP fragments on the named stream (typically "stdin" for pipeline fragments after the first, or "stdin pr" for runspace-pool messages after Open). commandID may be empty for pool-scoped sends.

Reference: node-winrm src/wsmv/send-data.ts.

func NewSignalPSRPRequest

func NewSignalPSRPRequest(uri, shellID, commandID string, params *Parameters) *soap.SoapMessage

NewSignalPSRPRequest builds the WS-Man Signal message used to request pipeline termination for a PSRP shell.

func NewSignalRequest

func NewSignalRequest(uri string, shellID string, commandID string, params *Parameters) *soap.SoapMessage

NewSignalRequest NewSignalRequest

func ParseExecuteCommandResponse

func ParseExecuteCommandResponse(response string) (string, error)

func ParseOpenShellResponse

func ParseOpenShellResponse(response string) (string, error)

func ParsePSRPCreateResponse

func ParsePSRPCreateResponse(response string) (string, error)

ParsePSRPCreateResponse extracts the server-allocated ShellId from a WS-Man Transfer/Create response for the PSRP plugin. The canonical location is the EPR returned in wst:ResourceCreated/wsa:ReferenceParameters/w:SelectorSet/ w:Selector[Name='ShellId']. Some hosts also surface rsp:ShellId in the body (CMD-shell-style); we fall back to that for compatibility.

Returning the server's value is required because MS-WSMV §3.1.4.3 makes the server the authority on the shell identifier; the client's rsp:Shell @ShellId is only a hint. Subsequent operations must use whatever value the server placed in the EPR — otherwise the WS-Man service responds with 2150858843 INVALID_SELECTORS.

func ParsePSRPReceiveResponse

func ParsePSRPReceiveResponse(response string) (data []byte, eof bool, err error)

ParsePSRPReceiveResponse extracts PSRP fragment bytes from a WS-Man Receive response and reports whether the server has signalled CommandState/Done. Multiple <rsp:Stream Name="stdout"> elements are concatenated in document order; their base64 contents are decoded.

Reference: node-winrm src/wsmv/receive.ts (ReceiveResponseReader.read).

func ParseSlurpOutputErrResponse

func ParseSlurpOutputErrResponse(response string, stdout, stderr io.Writer) (bool, int, error)

ParseSlurpOutputErrResponse ParseSlurpOutputErrResponse

func ParseSlurpOutputResponse

func ParseSlurpOutputResponse(response string, stream io.Writer, streamType string) (bool, int, error)

ParseSlurpOutputResponse ParseSlurpOutputResponse

func Powershell

func Powershell(psCmd string) string

Powershell wraps a PowerShell script and prepares it for execution by the winrm client

Types

type Client

type Client struct {
	Parameters
	// contains filtered or unexported fields
}

Client struct

func NewClient

func NewClient(endpoint *Endpoint, user, password string) (*Client, error)

NewClient will create a new remote client on url, connecting with user and password This function doesn't connect (connection happens only when CreateShell is called)

func NewClientWithParameters

func NewClientWithParameters(endpoint *Endpoint, user, password string, params *Parameters) (*Client, error)

NewClientWithParameters will create a new remote client on url, connecting with user and password This function doesn't connect (connection happens only when CreateShell is called)

func (*Client) CreatePowerShell

func (c *Client) CreatePowerShell() (*PowerShell, error)

CreatePowerShell opens a PSRP runspace pool on the target host using the request's background context.

func (*Client) CreatePowerShellWithContext

func (c *Client) CreatePowerShellWithContext(ctx context.Context) (*PowerShell, error)

CreatePowerShellWithContext opens a PSRP runspace pool, propagating ctx to every WS-Man HTTP request issued during pool open and subsequent pipeline execution. After the WS-Man Create succeeds, this drains pool-scoped Receive batches until the server emits a RUNSPACEPOOL_STATE Opened message so subsequent Pipeline.Invoke calls don't race the pool initialisation.

func (*Client) CreateRunspacePool

func (c *Client) CreateRunspacePool() (*psrp.RunspacePool, error)

CreateRunspacePool exposes the lower-level psrp.RunspacePool for callers that want to manage pipelines directly.

func (*Client) CreateRunspacePoolWithContext

func (c *Client) CreateRunspacePoolWithContext(ctx context.Context) (*psrp.RunspacePool, error)

CreateRunspacePoolWithContext is the context-aware variant. Like CreatePowerShellWithContext, it drains the pool until RUNSPACEPOOL_STATE reports Opened before returning.

func (*Client) CreateShell deprecated

func (c *Client) CreateShell() (*Shell, error)

CreateShell will create a WinRM Shell, which is the prealable for running commands.

Deprecated: use CreateShellWithContext.

func (*Client) CreateShellWithContext

func (c *Client) CreateShellWithContext(ctx context.Context) (*Shell, error)

CreateShellWithContext creates a WinRM Shell, propagating ctx to the underlying HTTP request so callers can cancel or timeout the open.

func (*Client) NewFileManager

func (c *Client) NewFileManager() *file.FileManager

NewFileManager returns a file.FileManager that executes commands via this Client's existing CMD shell + encoded-PowerShell pathway.

fm := client.NewFileManager()
err := fm.Upload(ctx, "/local/x.bin", `C:\dest\x.bin`, func(s, t int64) {
    fmt.Printf("%d/%d\n", s, t)
})

func (*Client) NewShell

func (c *Client) NewShell(id string) *Shell

NewShell will create a new WinRM Shell for the given shellID

func (*Client) Run deprecated

func (c *Client) Run(command string, stdout io.Writer, stderr io.Writer) (int, error)

Run will run command on the the remote host, writing the process stdout and stderr to the given writers. Note with this method it isn't possible to inject stdin.

Deprecated: use RunWithContext()

func (*Client) RunCmdWithContext

func (c *Client) RunCmdWithContext(ctx context.Context, command string) (string, string, int, error)

RunCmdWithContext will run command on the the remote host, returning the process stdout and stderr as strings If the context is canceled, the remote command is canceled.

func (*Client) RunPSWithContext

func (c *Client) RunPSWithContext(ctx context.Context, command string) (string, string, int, error)

RunPSWithContext will basically wrap your code to execute commands in powershell.exe. runs commands in cmd.exe

func (*Client) RunPSWithContextWithString

func (c *Client) RunPSWithContextWithString(ctx context.Context, command string, stdin string) (string, string, int, error)

RunPSWithContextWithString will basically wrap your code to execute commands in powershell.exe. Default RunWithString runs commands in cmd.exe

func (*Client) RunPSWithString deprecated

func (c *Client) RunPSWithString(command string, stdin string) (string, string, int, error)

RunPSWithString will basically wrap your code to execute commands in powershell.exe. Default RunWithString runs commands in cmd.exe

Deprecated: use RunPSWithContextWithString()

func (*Client) RunWithContext

func (c *Client) RunWithContext(ctx context.Context, command string, stdout io.Writer, stderr io.Writer) (int, error)

RunWithContext will run command on the the remote host, writing the process stdout and stderr to the given writers. Note with this method it isn't possible to inject stdin. If the context is canceled, the remote command is canceled.

func (*Client) RunWithContextWithInput

func (c *Client) RunWithContextWithInput(ctx context.Context, command string, stdout, stderr io.Writer, stdin io.Reader) (int, error)

RunWithContextWithInput will run command on the the remote host, writing the process stdout and stderr to the given writers, and injecting the process stdin with the stdin reader. If the context is canceled, the command on the remote machine is canceled. Warning stdin (not stdout/stderr) are bufferized, which means reading only one byte in stdin will send a winrm http packet to the remote host. If stdin is a pipe, it might be better for performance reasons to buffer it. If stdin is nil, this is equivalent to c.RunWithContext()

func (*Client) RunWithContextWithString

func (c *Client) RunWithContextWithString(ctx context.Context, command string, stdin string) (string, string, int, error)

RunWithContextWithString will run command on the the remote host, returning the process stdout and stderr as strings, and using the input stdin string as the process input If the context is canceled, the remote command is canceled.

func (*Client) RunWithInput deprecated

func (c *Client) RunWithInput(command string, stdout, stderr io.Writer, stdin io.Reader) (int, error)

RunWithInput will run command on the the remote host, writing the process stdout and stderr to the given writers, and injecting the process stdin with the stdin reader. Warning stdin (not stdout/stderr) are bufferized, which means reading only one byte in stdin will send a winrm http packet to the remote host. If stdin is a pipe, it might be better for performance reasons to buffer it. If stdin is nil, this is equivalent to c.Run()

Deprecated: use RunWithContextWithInput()

func (*Client) RunWithString deprecated

func (c *Client) RunWithString(command string, stdin string) (string, string, int, error)

RunWithString will run command on the the remote host, returning the process stdout and stderr as strings, and using the input stdin string as the process input

Deprecated: use RunWithContextWithString()

type ClientAuthRequest

type ClientAuthRequest struct {
	// contains filtered or unexported fields
}

ClientAuthRequest ClientAuthRequest

func NewClientAuthRequestWithDial

func NewClientAuthRequestWithDial(dial func(network, addr string) (net.Conn, error)) *ClientAuthRequest

NewClientAuthRequestWithDial NewClientAuthRequestWithDial

func (ClientAuthRequest) Post

func (c ClientAuthRequest) Post(ctx context.Context, client *Client, request *soap.SoapMessage) (string, error)

Post Post

func (*ClientAuthRequest) Transport

func (c *ClientAuthRequest) Transport(endpoint *Endpoint) error

Transport Transport

type ClientKerberos

type ClientKerberos struct {
	Username  string
	Password  string
	Realm     string
	Hostname  string
	Port      int
	Proto     string
	SPN       string
	KrbConf   string
	KrbCCache string
	// contains filtered or unexported fields
}

func NewClientKerberos

func NewClientKerberos(settings *Settings) *ClientKerberos

func (*ClientKerberos) Post

func (c *ClientKerberos) Post(ctx context.Context, clt *Client, request *soap.SoapMessage) (string, error)

func (*ClientKerberos) Transport

func (c *ClientKerberos) Transport(endpoint *Endpoint) error

type ClientKerberosEncrypted

type ClientKerberosEncrypted struct {
	Username  string
	Password  string
	Realm     string
	Hostname  string
	Port      int
	Proto     string
	SPN       string
	KrbConf   string
	KrbCCache string
	// contains filtered or unexported fields
}

ClientKerberosEncrypted is a Transporter that authenticates with Kerberos/SPNEGO and wraps every subsequent SOAP request in a WinRM message-level encryption (MLE) multipart/encrypted envelope using an RFC 4121 sealed GSS Wrap token.

For HTTPS endpoints, prefer ClientKerberos which performs auth-only SPNEGO and lets TLS handle channel encryption.

Configure via *Settings (matches ClientKerberos). Optional dial and proxy hooks are available via NewClientKerberosEncryptedWithDial and NewClientKerberosEncryptedWithProxyFunc.

func NewClientKerberosEncrypted

func NewClientKerberosEncrypted(s *Settings) *ClientKerberosEncrypted

NewClientKerberosEncrypted constructs a ClientKerberosEncrypted from the same Settings struct used by ClientKerberos.

func NewClientKerberosEncryptedWithDial

func NewClientKerberosEncryptedWithDial(s *Settings, dial func(network, addr string) (net.Conn, error)) *ClientKerberosEncrypted

NewClientKerberosEncryptedWithDial returns a ClientKerberosEncrypted that uses the supplied dial function for the underlying TCP connection.

func NewClientKerberosEncryptedWithProxyFunc

func NewClientKerberosEncryptedWithProxyFunc(s *Settings, pf func(req *http.Request) (*url.URL, error)) *ClientKerberosEncrypted

NewClientKerberosEncryptedWithProxyFunc returns a ClientKerberosEncrypted that uses the supplied proxy resolver.

func (*ClientKerberosEncrypted) Post

func (c *ClientKerberosEncrypted) Post(ctx context.Context, _ *Client, message *soap.SoapMessage) (string, error)

Post sends a SOAP message wrapped in the MLE envelope and returns the decrypted response body. ctx is propagated to the HTTP request.

func (*ClientKerberosEncrypted) Transport

func (c *ClientKerberosEncrypted) Transport(endpoint *Endpoint) error

Transport stores the endpoint and prepares the underlying HTTP transport. Kerberos handshake is deferred to the first Post call.

type ClientNTLM

type ClientNTLM struct {
	// contains filtered or unexported fields
}

ClientNTLM provides a transport via NTLMv2

func NewClientNTLMWithDial

func NewClientNTLMWithDial(dial func(network, addr string) (net.Conn, error)) *ClientNTLM

NewClientNTLMWithDial NewClientNTLMWithDial

func NewClientNTLMWithProxyFunc

func NewClientNTLMWithProxyFunc(proxyfunc func(req *http.Request) (*url.URL, error)) *ClientNTLM

NewClientNTLMWithProxyFunc NewClientNTLMWithProxyFunc

func (ClientNTLM) Post

func (c ClientNTLM) Post(ctx context.Context, client *Client, request *soap.SoapMessage) (string, error)

Post make post to the winrm soap service (forwarded to clientRequest implementation)

func (*ClientNTLM) Transport

func (c *ClientNTLM) Transport(endpoint *Endpoint) error

Transport creates the wrapped NTLM transport

type ClientNTLMEncrypted

type ClientNTLMEncrypted struct {
	// contains filtered or unexported fields
}

ClientNTLMEncrypted is a Transporter that performs NTLM authentication and wraps every SOAP request in a WinRM message-level encryption (MLE) multipart/encrypted envelope. Use this when connecting to a Windows host on HTTP (port 5985) where WinRM's default AllowUnencrypted=false config requires encryption at the message layer.

For HTTPS endpoints where TLS provides the channel encryption, prefer ClientNTLM (which performs auth-only NTLM and skips MLE).

Each Post performs its own NTLM handshake and seals the body with the freshly-negotiated session key. NTLM session security is HTTP- connection-bound: the SecuritySession from a completed handshake is only valid for messages on that TCP connection. Caching the sealer across Posts produces 400/401 desync errors when the keep-alive connection drops between calls. The fresh-handshake-per-Post pattern matches the reference pywinrm implementation and the legacy `Encryption` struct in this package.

Usage:

params := winrm.DefaultParameters
params.TransportDecorator = func() winrm.Transporter {
    return &winrm.ClientNTLMEncrypted{}
}

func NewClientNTLMEncryptedWithDial

func NewClientNTLMEncryptedWithDial(dial func(network, addr string) (net.Conn, error)) *ClientNTLMEncrypted

NewClientNTLMEncryptedWithDial returns a ClientNTLMEncrypted that uses the supplied dial function for the underlying TCP connection.

func NewClientNTLMEncryptedWithProxyFunc

func NewClientNTLMEncryptedWithProxyFunc(proxyfunc func(req *http.Request) (*url.URL, error)) *ClientNTLMEncrypted

NewClientNTLMEncryptedWithProxyFunc returns a ClientNTLMEncrypted that uses the supplied proxy resolver for the underlying HTTP transport.

func (*ClientNTLMEncrypted) Post

func (c *ClientNTLMEncrypted) Post(ctx context.Context, client *Client, message *soap.SoapMessage) (string, error)

Post sends a SOAP message wrapped in the MLE envelope and returns the decrypted response body. Each call performs a fresh NTLM handshake to stay aligned with HTTP-connection-bound NTLM session lifetime.

func (*ClientNTLMEncrypted) Transport

func (c *ClientNTLMEncrypted) Transport(endpoint *Endpoint) error

Transport stores the endpoint. The underlying HTTP transport is built fresh per Post so each request uses its own TCP connection. NTLM session security is connection-bound on the server: reusing a TCP across multiple Posts would leave the server expecting a session that our fresh per-Post ntlmssp.Client does not have, causing the server to skip the auth challenge on subsequent requests. A fresh transport per Post avoids that desync.

type Command

type Command struct {
	Stdin  *commandWriter
	Stdout *commandReader
	Stderr *commandReader
	// contains filtered or unexported fields
}

Command represents a given command running on a Shell. This structure allows to get access to the various stdout, stderr and stdin pipes.

func (*Command) Close

func (c *Command) Close() error

Close will terminate the running command

func (*Command) Error

func (c *Command) Error() error

Error returns command execution error if any

func (*Command) ExitCode

func (c *Command) ExitCode() int

ExitCode returns command exit code when it is finished. Before that the result is always 0.

func (*Command) Wait

func (c *Command) Wait()

Wait function will block the current goroutine until the remote command terminates.

type Encryption

type Encryption struct {
	// contains filtered or unexported fields
}

func NewEncryption

func NewEncryption(protocol string) (*Encryption, error)

Encrypted Message Types When using Encryption, there are three options available

  1. Negotiate/SPNEGO

  2. Kerberos

  3. CredSSP

    protocol: The protocol string used for the particular auth protocol

    The auth protocol used, will determine the wrapping and unwrapping method plus the protocol string to use. Currently only NTLM is supported

    based on the python code from https://pypi.org/project/pywinrm/

    see https://github.com/diyan/pywinrm/blob/master/winrm/encryption.py

    uses the most excellent NTLM library from https://github.com/bodgit/ntlmssp

func (*Encryption) ParseEncryptedResponse

func (e *Encryption) ParseEncryptedResponse(response *http.Response) ([]byte, error)

Takes in the encrypted response from the server and decrypts it

:param response: The response that needs to be decrytped :return: The unencrypted message from the server

func (*Encryption) Post

func (e *Encryption) Post(ctx context.Context, client *Client, message *soap.SoapMessage) (string, error)

func (*Encryption) PrepareEncryptedRequest

func (e *Encryption) PrepareEncryptedRequest(ctx context.Context, client *Client, endpoint string, message []byte) (string, error)

Creates a prepared request to send to the server with an encrypted message and correct headers

:param endpoint: The endpoint/server to prepare requests to :param message: The unencrypted message to send to the server :return: A prepared request that has an decrypted message

func (*Encryption) PrepareRequest

func (e *Encryption) PrepareRequest(ctx context.Context, client *Client, endpoint string) error

func (*Encryption) Transport

func (e *Encryption) Transport(endpoint *Endpoint) error

type Endpoint

type Endpoint struct {
	// host name or ip address
	Host string
	// port to determine if it's http or https default
	// winrm ports (http:5985, https:5986).Versions
	// of winrm can be customized to listen on other ports
	Port int
	// set the flag true for https connections
	HTTPS bool
	// set the flag true for skipping ssl verifications
	Insecure bool
	// if set, used to verify the hostname on the returned certificate
	TLSServerName string
	// pointer pem certs, and key
	CACert []byte // cert auth to intdetify the server cert
	Key    []byte // public key for client auth connections
	Cert   []byte // cert for client auth connections
	// duration timeout for the underling tcp conn(http/https base protocol)
	// if the time exceeds the connection is cloded/timeouts
	Timeout time.Duration
}

Endpoint struct holds configurations for the server endpoint

func NewEndpoint

func NewEndpoint(host string, port int, https bool, insecure bool, Cacert, cert, key []byte, timeout time.Duration) *Endpoint

NewEndpoint returns new pointer to struct Endpoint, with a default 60s response header timeout

type ExecuteCommandError

type ExecuteCommandError struct {
	Inner error
	Body  string
}

func (*ExecuteCommandError) Error

func (e *ExecuteCommandError) Error() string

func (*ExecuteCommandError) Is

func (e *ExecuteCommandError) Is(err error) bool

func (*ExecuteCommandError) Unwrap

func (b *ExecuteCommandError) Unwrap() error

type Parameters

type Parameters struct {
	Timeout            string
	Locale             string
	EnvelopeSize       int
	TransportDecorator func() Transporter
	Dial               func(network, addr string) (net.Conn, error)

	// RetryAttempts is the maximum number of times sendRequest will retry
	// after a transient transport failure. Zero (the default) disables
	// retry. Semantic errors (WSManFaultError, ExecuteCommandError) are
	// never retried. Network errors, 5xx responses and timeouts are.
	RetryAttempts int

	// RetryDelay is the base backoff between retry attempts. Each
	// successive attempt doubles the delay (capped at 30s). Default 1s
	// when RetryAttempts > 0 and RetryDelay is zero.
	RetryDelay time.Duration
}

Parameters struct defines metadata information and http transport config

func NewParameters

func NewParameters(timeout, locale string, envelopeSize int) *Parameters

NewParameters return new struct of type Parameters this struct makes the configuration for the request, size message, etc.

type PowerShell

type PowerShell struct {
	// contains filtered or unexported fields
}

PowerShell is the high-level handle for executing scripts over PSRP against a remote Windows host. It owns a single RunspacePool. Pipelines can be invoked one at a time via Execute.

Usage:

client, _ := winrm.NewClient(endpoint, user, pass)
ps, err := client.CreatePowerShell()
if err != nil { ... }
defer ps.Close()
out, err := ps.Execute("Get-Process | Select-Object -First 5")
for _, obj := range out.Objects {
    s, _ := obj.Str()
    fmt.Println(s)
}

func (*PowerShell) Close

func (ps *PowerShell) Close() error

Close terminates the underlying runspace pool.

func (*PowerShell) Execute

func (ps *PowerShell) Execute(script string) (*PowerShellOutput, error)

Execute runs a script in the runspace pool and collects the output.

func (*PowerShell) ExecuteStreaming

func (ps *PowerShell) ExecuteStreaming(script string, cb psrp.StreamCallback) (*PowerShellOutput, error)

ExecuteStreaming is like Execute but invokes cb for each value emitted by the pipeline as it arrives. The accumulated output is still returned; the callback is purely additional for live consumers.

type PowerShellOutput

type PowerShellOutput struct {
	Objects     []*psrp.Value
	Errors      []*psrp.Value
	Warnings    []*psrp.Value
	Verbose     []*psrp.Value
	Debug       []*psrp.Value
	Information []*psrp.Value
	HadErrors   bool
	State       psrp.PipelineState
}

PowerShellOutput aggregates all stream output from a single Execute.

type Settings

type Settings struct {
	WinRMUsername        string
	WinRMPassword        string
	WinRMHost            string
	WinRMPort            int
	WinRMProto           string
	WinRMInsecure        bool
	KrbRealm             string
	KrbConfig            string
	KrbSpn               string
	KrbCCache            string
	WinRMUseNTLM         bool
	WinRMPassCredentials bool
}

Settings holds all the information necessary to configure the provider

type Shell

type Shell struct {
	// contains filtered or unexported fields
}

Shell is the local view of a WinRM Shell of a given Client

func (*Shell) Close

func (s *Shell) Close() error

Close will terminate this shell. No commands can be issued once the shell is closed.

func (*Shell) CloseWithContext

func (s *Shell) CloseWithContext(ctx context.Context) error

CloseWithContext terminates this shell, propagating ctx to the underlying HTTP request.

func (*Shell) Execute deprecated

func (s *Shell) Execute(command string, arguments ...string) (*Command, error)

Execute command on the given Shell, returning either an error or a Command

Deprecated: user ExecuteWithContext

func (*Shell) ExecuteWithContext

func (s *Shell) ExecuteWithContext(ctx context.Context, command string, arguments ...string) (*Command, error)

ExecuteWithContext command on the given Shell, returning either an error or a Command

type Transporter

type Transporter interface {
	Post(ctx context.Context, client *Client, msg *soap.SoapMessage) (string, error)
	Transport(*Endpoint) error
}

Transporter does different transporters and init a Post request based on them.

Post takes a context.Context as its first argument so callers can cancel or timeout requests. Implementations should propagate the context to the underlying HTTP call (e.g. via http.NewRequestWithContext) and to any TLS/dial steps that respect it. A nil context is treated as context.Background().

type WSManFaultError

type WSManFaultError struct {
	// Code is the SOAP fault Code/Value (e.g. "s:Sender").
	Code string
	// Subcode is the SOAP fault Code/Subcode/Value
	// (e.g. "w:InvalidParameter").
	Subcode string
	// WSManFaultCode is the numeric WS-Man fault code from the
	// <wsmanfault:WSManFault Code="..."> attribute when present.
	WSManFaultCode string
	// WMIErrorCode is the WMI error code from <f:Detail/f:WMIError/...>
	// when present.
	WMIErrorCode string
	// Reason is the human-readable text from soap:Reason/soap:Text.
	Reason string
	// Detail captures any extra text payload under soap:Detail/Message.
	Detail string
	// Body is the raw SOAP fault XML, useful for debugging.
	Body string
}

WSManFaultError is a structured representation of a WS-Management SOAP fault. Returned errors from the transport can be type-asserted via errors.As to recover machine-readable codes:

var fe *winrm.WSManFaultError
if errors.As(err, &fe) {
    fmt.Println(fe.WSManFaultCode, fe.Reason)
}

Reference: pywinrm v0.5+ winrm/exceptions.WSManFaultError; MS-WSMV §2.2.4.

func ParseWSManFault

func ParseWSManFault(body string) *WSManFaultError

func (*WSManFaultError) Error

func (e *WSManFaultError) Error() string

func (*WSManFaultError) Is

func (e *WSManFaultError) Is(target error) bool

Is reports membership in the WSManFaultError class so errors.Is works with a sentinel of any *WSManFaultError.

Directories

Path Synopsis
Package encryption implements the WS-Management Message Level Encryption (MLE) envelope used by NTLM, Kerberos/SPNEGO and (in future) CredSSP transports.
Package encryption implements the WS-Management Message Level Encryption (MLE) envelope used by NTLM, Kerberos/SPNEGO and (in future) CredSSP transports.
Package file provides chunked Upload and Download helpers for transferring files to and from a remote Windows host over WinRM.
Package file provides chunked Upload and Download helpers for transferring files to and from a remote Windows host over WinRM.
Package psrp implements the PowerShell Remoting Protocol (MS-PSRP) message and fragment layers, CLIXML serialization and a runspace pool / pipeline abstraction that runs over the existing WinRM transport.
Package psrp implements the PowerShell Remoting Protocol (MS-PSRP) message and fragment layers, CLIXML serialization and a runspace pool / pipeline abstraction that runs over the existing WinRM transport.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL