diff --git a/docs/index.md b/docs/index.md index 20190bed..8b45744a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -116,9 +116,11 @@ This allows the provider to use the SSH agent configured by the user, and to sup You can find more details on the SSH Agent [here](https://www.digitalocean.com/community/tutorials/ssh-essentials-working-with-ssh-servers-clients-and-keys#adding-your-ssh-keys-to-an-ssh-agent-to-avoid-typing-the-passphrase). The SSH agent authentication takes precedence over the `private_key` and `password` authentication. +-> By default on Windows, the provider will assume the SSH agent is at `\\.\pipe\openssh-ssh-agent`. + ### SSH Private Key -In some cases where SSH agent is not available, for example when running Terraform from a Windows machine, or when using a CI/CD pipeline that does not support SSH agent forwarding, +In some cases where SSH agent is not available, for example when using a CI/CD pipeline that does not support SSH agent forwarding, you can use the `private_key` argument in the `ssh` block (or alternatively `PROXMOX_VE_SSH_PRIVATE_KEY` environment variable) to provide the private key for the SSH connection. The private key mut not be encrypted, and must be in PEM format. diff --git a/go.mod b/go.mod index ded25496..66a9caef 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.22 toolchain go1.22.3 require ( + github.com/Microsoft/go-winio v0.6.2 github.com/avast/retry-go/v4 v4.6.0 github.com/brianvoe/gofakeit/v7 v7.0.3 github.com/google/go-cmp v0.6.0 diff --git a/go.sum b/go.sum index baa89e91..81527e51 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/ProtonMail/go-crypto v1.1.0-alpha.0 h1:nHGfwXmFvJrSR9xu8qL7BkO4DqTHXE9N5vPhgY2I+j0= github.com/ProtonMail/go-crypto v1.1.0-alpha.0/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= diff --git a/proxmox/ssh/client.go b/proxmox/ssh/client.go index 297c7a2e..e12820b5 100644 --- a/proxmox/ssh/client.go +++ b/proxmox/ssh/client.go @@ -78,9 +78,13 @@ func NewClient( socks5Server string, socks5Username string, socks5Password string, nodeResolver NodeResolver, ) (Client, error) { - if agent && runtime.GOOS != "linux" && runtime.GOOS != "darwin" && runtime.GOOS != "freebsd" { + if agent && + runtime.GOOS != "linux" && + runtime.GOOS != "darwin" && + runtime.GOOS != "freebsd" && + runtime.GOOS != "windows" { return nil, errors.New( - "the ssh agent flag is only supported on POSIX systems, please set it to 'false'" + + "the ssh agent flag is only supported on POSIX and Windows systems, please set it to 'false'" + " or remove it from your provider configuration", ) } @@ -531,12 +535,7 @@ func (c *client) createSSHClientAgent( kh knownhosts.HostKeyCallback, sshHost string, ) (*ssh.Client, error) { - if c.agentSocket == "" { - return nil, errors.New("failed connecting to SSH agent socket: the socket file is not defined, " + - "authentication will fall back to password") - } - - conn, err := net.Dial("unix", c.agentSocket) + conn, err := dialSocket(c.agentSocket) if err != nil { return nil, fmt.Errorf("failed connecting to SSH auth socket '%s': %w", c.agentSocket, err) } diff --git a/proxmox/ssh/client_notwindows.go b/proxmox/ssh/client_notwindows.go new file mode 100644 index 00000000..35f0a5d0 --- /dev/null +++ b/proxmox/ssh/client_notwindows.go @@ -0,0 +1,24 @@ +//go:build !windows + +package ssh + +import ( + "errors" + "fmt" + "net" +) + +// dialSocket dials a Unix domain socket. +func dialSocket(address string) (net.Conn, error) { + if address == "" { + return nil, errors.New("failed connecting to SSH agent socket: the socket file is not defined, " + + "authentication will fall back to password") + } + + conn, err := net.Dial("unix", address) + if err != nil { + return nil, fmt.Errorf("error dialing unix socket: %w", err) + } + + return conn, nil +} diff --git a/proxmox/ssh/client_windows.go b/proxmox/ssh/client_windows.go new file mode 100644 index 00000000..180e81c1 --- /dev/null +++ b/proxmox/ssh/client_windows.go @@ -0,0 +1,24 @@ +//go:build windows + +package ssh + +import ( + "fmt" + "net" + + "github.com/Microsoft/go-winio" +) + +// dialSocket dials a Windows named pipe. If address is empty, it dials the default ssh-agent pipe. +func dialSocket(address string) (net.Conn, error) { + if address == "" { + address = `\\.\pipe\openssh-ssh-agent` + } + + conn, err := winio.DialPipe(address, nil) + if err != nil { + return nil, fmt.Errorf("error dialing named pipe: %w", err) + } + + return conn, nil +}