sftp
sftp copied to clipboard
Execute SUDO prior to transfer
Is it possible to invoke an sudo
prior to executing commands such as is possible in winscp?
http://winscp.net/eng/docs/faq_su
Hmm. Good question.
At the moment this isn't possible because we request that the remote ssh server start an sftp-session for us.
What I think winscp is doing is starting a normal terminal session (without pty) then running sudo /usr/libexec/sftp-server which does essentially the same thing, although it does require that sudo for that account not ask for a password, or be configured to not ask for a password for that command.
If you like I can send you a branch which sort of does what I think winscp is doing and we can take it from there.
@davecheney are you still working on this?
@davecheney and @eikenb I've been looking into this today.
I've been looking at this today, and started out by looking at how this is handled when using the sftp cli. Seems like there are two ways to use sudo on sftp. Look at this stack exchange question for reference.
I've also started on a branch to solve it: https://github.com/pkg/sftp/compare/master...krilor:subsystem?expand=1
It works, but I would like to get some input and want to ask if this is something that would be considered as a PR. Both methods I've added could be implemented by users when they need such functionality, but it might save someone the time I've spent today looking into this.
1 - Modifying /etc/sshd_config
It seems like modifying the Subsystem sftp command is one way to go, but looking at it, it could also be that some would like to add a new Subsystem with sudo-prefix.
Like this
Subsystem sftp /usr/lib/openssh/sftp-server
Subsystem suftp sudo /usr/lib/openssh/sftp-server
Don't know how likely it would be that someone would want to do this, but I've added the method NewSubsystemClient
, that allows users to specify which subsystem to use.
2 - Specifying a subsystem command using the -s command line flag
Something like this: sftp -s "sudo /usr/lib/openssh/sftp-server" targethost.fqdn
Man states the following about -s
s subsystem | sftp_server
Specifies the SSH2 subsystem or the path for an sftp server on the remote host. A path is useful when the remote sshd(8) does not have an sftp subsystem configured.
I've added NewSubsystemCommandClient
to allow users to specify a custom command to be used. This matches the WinSCP option and the sftp -s flag.
It uses and "exec" request, rather than "subsystem". "subsystem" is reserved for predefined subsystems (https://tools.ietf.org/html/rfc4254#section-6.5)
Usage example
package main
import (
"log"
"github.com/pkg/sftp"
"golang.org/x/crypto/ssh"
)
func main() {
cc := ssh.ClientConfig{
User: "gossh",
Auth: []ssh.AuthMethod{
ssh.Password("gosshpwd"),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(), // TODO
}
conn, err := ssh.Dial("tcp", "localhost:32774", &cc)
if err != nil {
log.Fatalln("unable to connect", err)
}
defer conn.Close()
sess, err := conn.NewSession()
if err != nil {
log.Fatalln("new session errored", err)
}
defer sess.Close()
// running this command to get the sftp-server path, as it can vary from system to system
b, err := sess.Output("grep -e 'Subsystem\tsftp' /etc/ssh/sshd_config | awk '{print $3}'")
if err != nil {
log.Fatalln("cmd output errored", err)
}
sftpServerPath := string(b)
//client, err := sftp.NewClient(conn)
//client, err := sftp.NewSubsystemClient(conn, "suftp")
client, err := sftp.NewSubsystemCommandClient(conn, "sudo -n -u hobgob "+sftpServerPath)
if err != nil {
log.Fatalln("sftp unable to connect", err)
}
defer client.Close()
err = client.Mkdir("/home/hobgob/isnothere")
if err != nil {
log.Fatalln("unable to create dir", err)
}
}
It would probably be better to make a sftp.WithSubsystem(…)
and either (sftp.WithCommand(…)
or sftp.WithSubsystemCommand
) instead of having separate exported functions for each… but it also looks like the current design is 😬 not going to make that sort of a thing easy.
PS: The code looks good in general, just a few linting things I would address in a PR.
@puellanivis Yes, I don't see how that could be done in a simple way. I also briefly thought about passing it as a ClientOption somehow, and pick that out in NewClient, but it just felt wrong, even tho ClientOptions are configuration of a client. And it might not even be possible.
One thing to consider is only adding the NewSubsystemCommandClient method (maybe give it a new name), as I think the other method is a bit contrived and not as useful.
On the other hand: I also think I've successfully made a third method for specifically supporting sudo with and without pwd, by reading from stderr and writing to stdin before passing the pipes on to NewClientPipe.
It could be that methods such as these should live outside this package, but maybe I could add NewSubsystemCommandClient it as an example for NewClientPipe?
maybe I could add NewSubsystemCommandClient it as an example for NewClientPipe
I mean, that’s a minimal addition that I think could definitely get off the ground faster than adding code. Something to have around while we discuss how to best implement it ourselves internally?
Ok, I'll raise a PR, and we'll take it from there. Hopefully within a couple of days.
If anyone are interested, I've also just successfully implemented and tested a sudo version that handles password, nopasswd, lecture and no lecture.
NewSudoClient
here: https://github.com/krilor/gossh/blob/master/rmt/suftp/suftp.go#L85 Needs some cleanup, but hey - it works.
When I modify the /etc/sshd_config "Subsystem sftp sudo /usr/lib/openssh/sftp-server",the go sftp could have root privilege.But the ansible-playbook create remote dir couldn't work. my ansible config is remote_tmp = /tmp/.ansible/tmp.It returns : "msg": "Failed to set execute bit on remote files (rc: 1, err: chmod
Failed to set execute bit on remote files (rc: 1, err: chmod
My first thinking on this error report is that the OS is preventing you from setting the executable bit on any file that is underneath /tmp
whether you’re root or not. This is a pretty normal security feature, in the same sort of idea as W^X that is: temporary writeable locations should not be executable to prevent arbitrary code execution.
Failed to set execute bit on remote files (rc: 1, err: chmod
My first thinking on this error report is that the OS is preventing you from setting the executable bit on any file that is underneath
/tmp
whether you’re root or not. This is a pretty normal security feature, in the same sort of idea as W^X that is: temporary writeable locations should not be executable to prevent arbitrary code execution.
When I change the dir to the /home , Is the same. I think Is the conflict with the ansible sudo and the golang sftp sudo.Maybe use NewSubsystemCommandClient could solve!