Go HomeDir reads /etc/passwd, not $HOME

Hello!

snapd sets the environment variable $HOME to $SNAP_USER_DATA when a command is executed. $SNAP_USER_DATA is something like /home/<user>/snap/<snap-name>/<snap-revision>, a path that’s writable by the snap. This works in most of the cases, except in go.

The recommended way to get the user home in go is something like:

usr, _ := user.Current()
fmt.Fprintf(usr.HomeDir)

The implementation of the Current method sets HomeDir by reading and parsing /etc/passwd, which will point to /home/<user>, totally ignoring our patched $HOME.

Should we also present the snap with a patched version of /etc/passwd? or is there a better solution for go snaps accessing the home dir?

pura vida

Side-stepping the discussion on the issues of maintaining /etc/passwd for new users across all the snaps, this change would make it difficult for snaps to find the real home, which a lot of snaps for classic distro using the home interface are interested in. Today, HOME is set to SNAP_USER_DATA (like you said) but snaps can and do use getent, getpwent(), getpwnam(), getpwuid(), etc to find the real home. Updating /etc/passwd would make all of those snaps break.

If this were going to be addressed, I think we’d want it opt-in so that existing snaps didn’t break. We’d probably also want to use a custom nss module instead of modifying /etc/passwd (since /etc/passwd may not even have the user in it).

For the record, upstream was very nice here and he patched the source code to first search for $HOME:

So, I guess that an option would be to accept that snaps are not fully transparent, for cases like this, and document best practices for upstreams to adjust their code to work with snaps.

Another such case that comes to mind is bitcoin, that requires a patch to save the blockchain to $SNAP_USER_COMMON instead of $SNAP_USER_DATA. This is not transparent because upstreams that want to save data will just do throw everything in $HOME; but we are introducing the concept of versioned and unversioned home, and they would have to adjust their source code to use it correctly.

Run into this issue and found a different solution for go1.12+.

home, _ := os.UserHomeDir()
fmt.Println(home)

On Unix, including macOS, it returns the $HOME environment variable. On Windows, it returns %USERPROFILE%. On Plan 9, it returns the $home environment variable.

1 Like