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

sudo with password support #210

Open
kaplan-michael opened this issue Jun 21, 2024 · 7 comments
Open

sudo with password support #210

kaplan-michael opened this issue Jun 21, 2024 · 7 comments

Comments

@kaplan-michael
Copy link

kaplan-michael commented Jun 21, 2024

Split up from a closed/related issue for better tracking. I do plan to attempt an implementation of this.

I do have a question in terms of sudo, do you plan/want to support sudo with a password? probably injected through stdin?

Originally posted by @kaplan-michael in #195 (comment)

@twz123
Copy link
Member

twz123 commented Jun 21, 2024

This might get quite cumbersome and brittle. Many of the commands k0sctl runs already read input from stdin. I'm not sure if there is a reliable way to split this into a stdin for sudo and a stdin for the command itself.

@kaplan-michael
Copy link
Author

Yeah,
My theory is that we could try catch the stdout and only if we find a sudo prompt, we inject the password, but from what I have been playing with it. it seems quite impossible to make it reliable... in a non interactive Exec.
As of now, I assume Interactive Exec(when we have a pty) should be better.

I also wonder if we could perhaps have another command decorator(similar to sudo) that would handle that for us on the remote side?

Maybe someone has some better ideas how to make it work reliably?

@kke
Copy link
Contributor

kke commented Jun 24, 2024

Looking for sudo prompts would require analyzing all output (different locales and distros may make this very difficult to do reliably) and maybe buffer/gatekeep stdin input. And it's slightly suspicious security-wise to send the sudo password to any command that renders output that looks like a sudo prompt.

Perhaps the best option would be to spawn a sudo shell after connect that would be then used as the launcher for all subsequent commands, this is possible by returning a cmd.Executor() that wraps all commands and sends them to the background sudo shell for running. Managing concurrency may require some kind of subshell-pooling. It would be possible to do this by registering this custom sudo method to the sudo.DefaultProvider() or by creating a new SudoProvider and passing that to each client via WithSudoProvider() client-option.

@kaplan-michael
Copy link
Author

kaplan-michael commented Jun 25, 2024

Looking for sudo prompts would require analyzing all output (different locales and distros may make this very difficult to do reliably) and maybe buffer/gatekeep stdin input. And it's slightly suspicious security-wise to send the sudo password to any command that renders output that looks like a sudo prompt.

Yeah, that's true.

I went with the route of doing a new provider with a decorator and calling it good on my end.
I wouldn't call it safe(mostly bcs sudo pass will show up in the process table), but we want to deprecate sudo with pass and ssh passwords anyways and just need to support the current behavior for a while(the same)

Thanks a lot for help so far.

// NewSudoProviderWithPass creates a new sudo provider configured with a sudo password.
func NewSudoProviderWithPass(password string) *sudo.Provider {
	provider := plumbing.NewProvider[cmd.Runner, cmd.Runner](ErrNoSudo)
	provider.Register(func(c cmd.Runner) (cmd.Runner, bool) {
		if c.IsWindows() {
			return nil, false
		}
		decorator := func(command string) string {
			return SudoPass(command, password)
		}
		return cmd.NewExecutor(c, decorator), true
	})
	return provider
}

// SudoPass is a DecorateFunc that will wrap the given command in a sudo call.
func SudoPass(cmd string, pass string) string {
	return fmt.Sprintf(`echo %s | sudo -S -- "${SHELL-sh}" -c %s`, shellescape.Quote(pass), shellescape.Quote(cmd))

}

and

rig.WithSudoProvider(sudo.NewSudoProviderWithPass(hostConfig.SudoPassword)

@twz123
Copy link
Member

twz123 commented Jun 25, 2024

func SudoPass(cmd string, pass string) string {
	return fmt.Sprintf(`echo %s | sudo -S -- "${SHELL-sh}" -c %s`, shellescape.Quote(pass), shellescape.Quote(cmd))

}

That will leak the password and make it visible via /proc to all users (if /proc isn't mounted with hidepid>0). You really need to pass the password via stdin.

@kaplan-michael
Copy link
Author

That will leak the password and make it visible via /proc to all users (if /proc isn't mounted with hidepid>0). You really need to pass the password via stdin.

Yes, I point to that in my comment as well.

I wouldn't call it safe(mostly bcs sudo pass will show up in the process table), but we want to deprecate sudo with pass and ssh passwords anyways and just need to support the current behavior for a while(the same)

The thing is that we currently do it in a similar way, so It doesn't change the security posture much.(and we use it on a single user system 99.99% of the time, so it fits within our risks)

I would like to point out, that I wouldn't say this is a good way to do this in rig.

@james-nesbitt
Copy link
Contributor

I have never seen a real world case where an administrator would prefer a password input over strict (heavy whitelisting) passwordless sudo

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

4 participants