cli
cli copied to clipboard
step-cli cant use cygwin ssh-agent
If a user attempts to use step-ca under a cygwin shell, many times they will have the cygwin versions of ssh/ssh-agent installed. step-cli is unable to connect correctly to the cygwin version of ssh-agent. I believe this is because step-cli detects it is running under windows and attempts to connect to the hard-coded OpenSSH for Windows named pipe (\\.\\pipe\\openssh-ssh-agent) instead of any SSH_AUTH_SOCK that may be defined.
I'm unsure how to fix this since cygwin uses a weird UNIX pipe implementation and I couldn't find any information about connecting to them in go.
As a workaround users can create a proxy pipe that will connect their cygwin environment with the ssh-agent that is running as a service when they install OpenSSH for Windows by running something similar to the following:
socat UNIX-LISTEN:/tmp/openssh-ssh-agent,umask=066,fork EXEC:"PLINK.EXE -serial //./pipe/openssh-ssh-agent",pipes
then running export SSH_AUTH_SOCK=/tmp/openssh-ssh-agent to identify which socket to use to ssh
Thanks for the feedback! Do you think your workaround is the best solution for now? Is this a documentation bug or should we change CLI behavior? We don't have a ton of Windows experience so if there's a behavior change that's required we'll probably have to backlog this until someone with more expertise can suggest (or implement) the right fix.
I haven't been a Windows user in a long time, so I'm curious: what's the use case for cygwin now that Windows Subsystem for Linux exists?
@mmalone I think we can check if the environment variable is set and then use we can try to connect to it, using unix sockets, and if it fails try to use the regular windows pipe one. The challenge is testing the different scenarios.
@unreality If you want to give it a try, the windows and unix implementations are here: https://github.com/smallstep/cli/blob/master/crypto/sshutil/agent_windows.go https://github.com/smallstep/cli/blob/master/crypto/sshutil/agent_unix.go
Here I'm changing the windows implementation to check if SSH_AUTH_SOCK exists, if it does, I'm trying to connect to unix sockets, if not then I fallback to the regular logic:
package sshutil
import (
"context"
"net"
"os"
"github.com/Microsoft/go-winio"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh/agent"
)
// dialAgent returns an ssh.Agent client.
func dialAgent() (*Agent, error) {
// Attempt unix sockets for environments like cygwin.
if socket := os.Getenv("SSH_AUTH_SOCK"); socket != "" {
if conn, err := net.Dial("unix", socket); err == nil {
return &Agent{
ExtendedAgent: agent.NewClient(conn),
Conn: conn,
}, nil
}
}
// Windows OpenSSH agent
conn, err := winio.DialPipeContext(context.Background(), `\\.\\pipe\\openssh-ssh-agent`)
if err != nil {
return nil, errors.Wrap(err, "error connecting with ssh-agent")
}
return &Agent{
ExtendedAgent: agent.NewClient(conn),
Conn: conn,
}, nil
}
@unreality if you can try this, can you let us know if it works.
It could be useful to check for SSH_AUTH_SOCK and attempt to connect to that before trying the named pipe, but i dont think that code will work specifically for cygwin.
AFAIK, Cygwin doesnt use 'real' unix sockets on windows (they were only recently added in win10). This stackoverflow answer details how they emulate unix sockets https://stackoverflow.com/questions/23086038/what-mechanism-is-used-by-msys-cygwin-to-emulate-unix-domain-sockets
I made a quick go program to net.Dial() the cygwin provided socket, but it failed to connect because of incorrect permissions.
I'm unsure if implementing something to properly connect to the cygwin socket is worth the time, maybe the workaround is the best option, particularly now that OpenSSH for Windows exists, and you can launch that from within cygwin. Perhaps even recommend removing the cygwin OpenSSH version and only use OpenSSH for Windows? Then the workaround isnt needed.
Looking at those answers, it looks like Cygwin creates a TCP server with a custom handshake protocol, and the file in SSH_AUTH_SOCK contains the port and the GUID to do the handshake. If this is the case the only solution I see is to create a custom dialer on top of the TCP one. This dialer will read the file, connects to the port using the real TCP dialer, perform the handshake with the necessary writes/reads using the GUID and then, in theory, rely on the real TCP dialer.
If you are up for creating a PR, we'll be happy to merge, but I really recommend you to use the Microsoft terminal instead https://github.com/microsoft/terminal :) and the OpenSSH implementation provided by Microsoft.
@maraino yes, the windows terminal is better, but some of the users at my org use cygwin so i thought i'd let others know what i found out about it :)