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

overlord/snapshotstate/backend: fall back on sudo when no runuser #5957

Merged
merged 4 commits into from Oct 18, 2018

Conversation

chipaca
Copy link
Contributor

@chipaca chipaca commented Oct 10, 2018

Without this change, snapshots don't work on 14.04.

Without this change, snapshots don't work on 14.04.
// runuser and sudo happen to work the same way in this case.
// The main reason to prefer runuser over sudo is that runuser
// is part of util-linux, which is considered essential,
// whereas sudo is an addon which could be removed.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding a note in case we forget the reasoning again.

// is part of util-linux, which is considered essential,
// whereas sudo is an addon which could be removed.
for _, cmd := range []string{"runuser", "sudo"} {
if lp, err := execLookPath(cmd); err == nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick, looking for the actual command could be done in sync.Once(), eg.

var cmdSeekedFor sync.Once
var sudoOrRunuser string
func pickUserWrapper() {
    for _, cmd := range []string{"runuser", "sudo"} {
        if lp, err := execLookPath(cmd); err != nil {
            sudoOrRunuser = lp
            break
        }
    }
}

func maybeRunuserCommand(.....) {
    cmdSeekedFor.Do(pickUserWrapper)
    if sysGeteuid() == 0 && username != "root" && sudoOnRunuser != "" {
        ....
    }
    return exec.Command(args[0], args[1:]...)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, might as well do it in init, n'eh?

// The main reason to prefer runuser over sudo is that runuser
// is part of util-linux, which is considered essential,
// whereas sudo is an addon which could be removed.
for _, cmd := range []string{"runuser", "sudo"} {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we log an error/warning when neither is found?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

Also, log when neither sudo nor runuser is available for use.
Copy link
Collaborator

@zyga zyga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A quick review, +0.8 with some questions asked.

// runuser and sudo happen to work the same way in this case.
// The main reason to prefer runuser over sudo is that runuser
// is part of util-linux, which is considered essential,
// whereas sudo is an addon which could be removed.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hold on, but the rationale for adding this branch is that we ... don't have runuser everywhere, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, 14.04 has an old util-linux that doesn't have runuser.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be worth mentioning in the comment that runuser is avaialble since util-linux 2.23 and some old systems that we support are on an even older version.

return ""
}

var sudo = pickUserWrapper()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the variable name is no longer appropriate. Perhaps launcher?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

launcher says nothing about what the thing is though...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could unify things:

  • pickUserWrapper is alright, i think
  • so sudo -> userWrapper
  • so maybeRunuserCommand -> userWrapCommand ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runAsUser

// runuser only works for euid 0, and doesn't make sense for root
return exec.Command(args[0], args[1:]...)
if sysGeteuid() == 0 && username != "root" && sudo != "" {
sudoArgs := make([]string, len(args)+4)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While perhaps a little bit YAGNI for now, since I assume the code works, I would consider having the launcher be an interface with a function RunWithArgs() that encapsulates this kind of knowledge. Right now there's a hidden assumption that "sudo" behaves like sudo, down the the command line handling.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed, YAGNI. If they needed different handling (e.g., if we were trying to use su or sudo), then it'd do something different. Still probably not an interface thing.


return exec.Command("runuser", sudoArgs...)
// TODO: use warnings instead
logger.Noticef("No user wrapper found; running tar for user data as root. Please make sure 'sudo' or 'runuser' (from util-linux) is on $PATH to avoid this.")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, when this is the case what would happen? Should things just fail loudly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure. It'll break on NFS and you'll get an error. Other than that it should be fine.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that's a good thing to know. We can already check if home is using NFS so perhaps all of this can be optional eventually?

…ew concerns

This change renames the maybeRunUser helper to be tarAsUser, and
specializes it to _only_ run tar instead of arbitrary commands. This
makes it that little bit clearer why the fail-open case is Ok, and why
it's not generally useful as-is. Also unifies sudo/runuser variables
to be userWrapper.

Thank you @zyga, I think it is clearer this way.
Copy link
Collaborator

@bboozzoo bboozzoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One suggestion to extend the comment a little bit. Otherwise, LGTM

// runuser and sudo happen to work the same way in this case.
// The main reason to prefer runuser over sudo is that runuser
// is part of util-linux, which is considered essential,
// whereas sudo is an addon which could be removed.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be worth mentioning in the comment that runuser is avaialble since util-linux 2.23 and some old systems that we support are on an even older version.

Copy link
Collaborator

@zyga zyga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with a few comments and one suggestion.

return "", errors.New("no such thing")
})()

c.Check(backend.PickUserWrapper(), check.Equals, "")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, I would expect this to return an error and not just an empty string. Reading on.


c.Check(backend.TarAsUser("test", "--bar"), check.DeepEquals, &exec.Cmd{
Path: "/bin/tar",
Args: []string{"tar", "--bar"},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

Args: uwArgs,
}
}
// TODO: use warnings instead
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, do use them :) any reason not to yet?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After writing this I realised it would be nice to have a helper to write tests like c.Assert(..., WarnsAbout, ...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to add a warning you need a handle on state, which is four calls up and to the lest of this. Needs some work to have a helper.

if sysGeteuid() == 0 && username != "root" {
if userWrapper != "" {
uwArgs := make([]string, len(args)+5)
copy(uwArgs[5:], args)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick, it would read more naturally, with progressing index variable, if copy was after uwArgs[4] = ... assignment below.

@chipaca chipaca merged commit 68e2f97 into snapcore:master Oct 18, 2018
@chipaca chipaca deleted the fallback-to-sudo branch October 18, 2018 00:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants