Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No support for commands with sudo #21

Open
agentram opened this issue Aug 20, 2021 · 4 comments
Open

No support for commands with sudo #21

agentram opened this issue Aug 20, 2021 · 4 comments

Comments

@agentram
Copy link

When trying to execute command with sudo, e.g. sudo sleep 3, I receive the error:

sudo: no tty present and no askpass program specified

2021/08/20 17:52:03 Process exited with status 1
exit status 1

Example code:

package main

import (
	"log"
	"fmt"
	"github.com/melbahja/goph"
)

func main() {

	// Start new ssh connection with password
	auth := goph.Password("12345")

	client, err := goph.New("testssh", "172.16.1.10", auth)
	if err != nil {
		log.Fatal(err)
	}

	// Defer closing the network connection.
	defer client.Close()

	// Execute your command.
	out, err := client.Run("sudo sleep 3")

	if err != nil {
	        fmt.Println(string(out))
		log.Fatal(err)
	}

	// Get your output as []byte.
	fmt.Println(string(out))
}
@melbahja
Copy link
Owner

This because sudo asks for root password and never gets one, you can connect via root if you want to run commands with root privileges.

@iFrozenPhoenix
Copy link

iFrozenPhoenix commented Oct 20, 2021

It should be possible to check if the stdout response is prefixed with "[sudo] password for ". If this is recognized, the password should just be sent again. Terraform is doing it so for example.
Another approach would be to use KeyBoardInteractive.

@SmsS4 SmsS4 mentioned this issue Jan 29, 2022
@f0rg-02
Copy link

f0rg-02 commented Jul 16, 2023

The problem isn't the library per se, but how sudo works. When running a command with sudo, the output returns an error about sudo needing a terminal or whatever to take the input for password. To solve this I just simply asked the user to input the password via the term package and a function I modified from SO:

// Taken from here: https://stackoverflow.com/a/32768479

func credentials() (string, error) {
	fmt.Print("Enter Password: ")
	bytePassword, err := term.ReadPassword(int(syscall.Stdin))
	if err != nil {
		return "", err
	}

	password := string(bytePassword)
	return strings.TrimSpace(password), nil
}

for my commands that require sudo I just did:

out, err := client.Run("echo " + password + "| sudo -S apt upgrade --assume-yes")

if err != nil {
    panic(err)
}

// Get your output as []byte.
fmt.Println("Output of command: \n\n", string(out))

Haven't fully tested it, and I'm tired, but it works for my test program.

sudo -S helps prevent the command from being added to history, including the echo.

Looks a little sloppy, but it works for what I need. I do think a function to at least specify a PTY properly with stdin and stdout would be nice to have, or at least some sort of example added to handle I/O going to and from the session.

EDIT: SSH by default doesn't allow root login, and it isn't recommended. Also NOPASSWD in sudoers isn't recommended either.

@zapling
Copy link

zapling commented Mar 13, 2024

I solved my use case by wrapping the client struct like this.

type SudoClient struct {
	*goph.Client
	sudoPassword string
}

func (s *SudoClient) SetPassword(password string) {
	s.sudoPassword = password
}

// RunContext runs the provided command and if a non empty password is provided it
// prefixes the command with sudo.
func (g *SudoClient) RunContext(ctx context.Context, name string) ([]byte, error) {
	if g.sudoPassword != "" {
		name = fmt.Sprintf(`echo %s | sudo --prompt="" -S %s`, g.sudoPassword, name)
	}
	return g.Client.RunContext(ctx, name)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants