sftp icon indicating copy to clipboard operation
sftp copied to clipboard

Execute SUDO prior to transfer

Open ghost opened this issue 10 years ago • 10 comments

Is it possible to invoke an sudo prior to executing commands such as is possible in winscp?

http://winscp.net/eng/docs/faq_su

ghost avatar Jun 13 '14 03:06 ghost

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 avatar Jun 14 '14 11:06 davecheney

@davecheney are you still working on this?

eikenb avatar Jan 14 '17 01:01 eikenb

@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)
	}
}

krilor avatar Apr 10 '20 19:04 krilor

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 avatar Apr 11 '20 14:04 puellanivis

@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?

krilor avatar Apr 11 '20 17:04 krilor

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?

puellanivis avatar Apr 12 '20 16:04 puellanivis

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.

krilor avatar Apr 12 '20 18:04 krilor

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

lwx2615 avatar May 11 '22 01:05 lwx2615

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.

puellanivis avatar May 11 '22 09:05 puellanivis

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!

lwx2615 avatar May 13 '22 10:05 lwx2615