Skip to content

fix: fall back to getent for users that are not findable in /etc/passwd#183

Open
tonyandrewmeyer wants to merge 6 commits into
canonical:mainfrom
tonyandrewmeyer:fix-username-lookup-no-passwd
Open

fix: fall back to getent for users that are not findable in /etc/passwd#183
tonyandrewmeyer wants to merge 6 commits into
canonical:mainfrom
tonyandrewmeyer:fix-username-lookup-no-passwd

Conversation

@tonyandrewmeyer
Copy link
Copy Markdown
Contributor

@tonyandrewmeyer tonyandrewmeyer commented Apr 15, 2026

The go User.Lookup method on Unix-like systems without ego just looks in the /etc/password file (whereas cgo does a getpwnam check via libc). On some systems, like the Canonical laptops, the user that is trying to run Concierge may not be in /etc/passwd and be loaded from a directory service instead.

This PR adds a fallback system where we will run the getent binary. That comes from the libc-bin package in Ubuntu, so is very likely to be available (and the worst case is that it's not and we'll fail just as we did before).

Fixes #182

@tonyandrewmeyer
Copy link
Copy Markdown
Contributor Author

FYI, you can test this with the --dry-run argument on a Canonical laptop. With the latest release (or main) you should get an error before Concierge manages to do anything. With this branch (sudo go run ./main.py prepare -p dev --dry-run) you should see the normal dry-run output. You won't see the call to getent because that happens so early (there's a comment that explains this); I looked into using that instead but it made it quite complex for almost no benefit.

@tonyandrewmeyer tonyandrewmeyer marked this pull request as ready for review April 16, 2026 02:02
Copy link
Copy Markdown
Contributor

@dimaqq dimaqq left a comment

Choose a reason for hiding this comment

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

Fly-by: the change looks good to me.

Copy link
Copy Markdown
Contributor

@james-garner-canonical james-garner-canonical left a comment

Choose a reason for hiding this comment

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

Landing this as-is seems reasonable to me.

I do wonder if we should swallow all errors from trying to run getent the way we are, and also if we should have a test exercising how we fail if getent isn't available.

I don't think these should block landing this feature since Ubuntu should always have getent and 'user not found' is probably our only expected error.

Comment thread internal/system/util.go
func lookupUserGetent(username string) (*user.User, error) {
out, err := exec.Command("getent", "passwd", username).Output()
if err != nil {
return nil, fmt.Errorf("user: unknown user %s", username)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is swallowing any error from getent this way the right approach? I assume there could be others other than 'unknown user'. Should we instead wrap err here, or inspect it to decide how to fail?

Comment on lines +11 to +13
if _, err := exec.LookPath("getent"); err != nil {
t.Skip("getent not available on this system")
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we have a test that shows how we fail if getent isn't available? Maybe if we have a mockable getentBinary variable in util.go?

I guess right now that would surface as "user: unknown user %s".

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

Successfully merging this pull request may close these issues.

Prepare does not work for users that are not in /etc/passwd

3 participants