[PATCH 2/3] scripts: environment: Add --user.

  • Done
  • quality assurance status badge
Details
2 participants
  • Ludovic Courtès
  • Mike Gerwitz
Owner
unassigned
Submitted by
Mike Gerwitz
Severity
normal
M
M
Mike Gerwitz wrote on 26 Jan 2018 04:29
(address . guix-patches@gnu.org)
ec4d18766172c82fc43bd944c416d23996c21789.1516937216.git.mtg@gnu.org
This change allows overriding the home directory of all filesystem mappings to
help hide the identity of the calling user in a container.

* doc/guix.texi (Invoking guix environment)[--container]: Mention --user.
[--user]: Add item.
* guix/scripts/environment.scm (show-help): Add --user.
(%options): Add --user.
(launch-environment/container) Add 'user' parameter. Update doc. Override
'user-mappings' using 'override-user-mappings'. Consider override for chdir.
(mock-passwd, user-override-home, overrid-euser-dir): New procedures.
(guix-environment): Disallow --user without --container. Provide user to
'launch-environment/container'.
* tests/guix-environment.sh: Add user test.
---
doc/guix.texi | 34 ++++++++++--
guix/scripts/environment.scm | 122 ++++++++++++++++++++++++++++++++++---------
tests/guix-environment.sh | 10 ++++
3 files changed, 137 insertions(+), 29 deletions(-)

Toggle diff (279 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index 3b6ae1ab9..8218c6637 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -7156,10 +7156,11 @@ Attempt to build for @var{system}---e.g., @code{i686-linux}.
@cindex container
Run @var{command} within an isolated container. The current working
directory outside the container is mapped inside the container.
-Additionally, a dummy home directory is created that matches the current
-user's home directory, and @file{/etc/passwd} is configured accordingly.
-The spawned process runs as the current user outside the container, but
-has root privileges in the context of the container.
+Additionally, unless overridden with @code{--user}, a dummy home
+directory is created that matches the current user's home directory, and
+@file{/etc/passwd} is configured accordingly. The spawned process runs
+as the current user outside the container, but has root privileges in
+the context of the container.
@item --network
@itemx -N
@@ -7183,6 +7184,31 @@ example, the @code{fontconfig} package inspects
@code{--link-profile} allows these programs to behave as expected within
the environment.
+@item --user=@var{user}
+@itemx -u @var{user}
+For containers, use the username @var{user} in place of the current
+user. The generated @file{/etc/passwd} entry within the container will
+contain the name @var{user}; the home directory will be
+@file{/home/USER}; and no user GECOS data will be copied. @var{user}
+need not exist on the system.
+
+Additionally, any shared or exposed path (see @code{--share} and
+@code{--expose} respectively) whose target is within the current user's
+home directory will be remapped relative to @file{/home/USER}; this
+includes the automatic mapping of the current working directory.
+
+@example
+# will expose paths as /home/foo/wd, /home/foo/test, and /home/foo/target
+cd $HOME/wd
+guix environment --container --user=foo \
+ --expose=$HOME/test \
+ --expose=/tmp/target=$HOME/target
+@end example
+
+While this will limit the leaking of user identity through home paths
+and each of the user fields, this is only one useful component of a
+broader privacy/anonymity solution---not one in and of itself.
+
@item --expose=@var{source}[=@var{target}]
For containers, expose the file system @var{source} from the host system
as the read-only file system @var{target} within the container. If
diff --git a/guix/scripts/environment.scm b/guix/scripts/environment.scm
index 771574c15..f50018faf 100644
--- a/guix/scripts/environment.scm
+++ b/guix/scripts/environment.scm
@@ -161,6 +161,10 @@ COMMAND or an interactive shell in that environment.\n"))
(display (G_ "
-P, --link-profile link environment profile to ~/.guix-profile within
an isolated container"))
+ (display (G_ "
+ -u, --user=USER instead of copying the name and home of the current
+ user into an isolated container, use the name USER
+ with home directory /home/USER"))
(display (G_ "
--share=SPEC for containers, share writable host file system
according to SPEC"))
@@ -243,6 +247,10 @@ COMMAND or an interactive shell in that environment.\n"))
(option '(#\P "link-profile") #f #f
(lambda (opt name arg result)
(alist-cons 'link-profile? #t result)))
+ (option '(#\u "user") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'user arg
+ (alist-delete 'user result eq?))))
(option '("share") #t #f
(lambda (opt name arg result)
(alist-cons 'file-system-mapping
@@ -390,43 +398,50 @@ environment variables are cleared before setting the new ones."
(pid (match (waitpid pid)
((_ . status) status)))))
-(define* (launch-environment/container #:key command bash user-mappings
+(define* (launch-environment/container #:key command bash user user-mappings
profile paths link-profile? network?)
"Run COMMAND within a container that features the software in PROFILE.
Environment variables are set according to PATHS, a list of native search
paths. The global shell is BASH, a file name for a GNU Bash binary in the
store. When NETWORK?, access to the host system network is permitted.
USER-MAPPINGS, a list of file system mappings, contains the user-specified
-host file systems to mount inside the container. LINK-PROFILE? creates a
-symbolic link from ~/.guix-profile to the environment profile."
+host file systems to mount inside the container. If USER is not #f, each
+target of USER-MAPPINGS will be re-written relative to '/home/USER', and USER
+will be used for the passwd entry. LINK-PROFILE? creates a symbolic link from
+~/.guix-profile to the environment profile."
(mlet %store-monad ((reqs (inputs->requisites
(list (direct-store-path bash) profile))))
(return
(let* ((cwd (getcwd))
- (passwd (getpwuid (getuid)))
+ (home (getenv "HOME"))
+ (passwd (mock-passwd (getpwuid (getuid))
+ user
+ bash))
(home-dir (passwd:dir passwd))
;; Bind-mount all requisite store items, user-specified mappings,
;; /bin/sh, the current working directory, and possibly networking
;; configuration files within the container.
(mappings
- (append user-mappings
- ;; Current working directory.
- (list (file-system-mapping
- (source cwd)
- (target cwd)
- (writable? #t)))
- ;; When in Rome, do as Nix build.cc does: Automagically
- ;; map common network configuration files.
- (if network?
- %network-file-mappings
- '())
- ;; Mappings for the union closure of all inputs.
- (map (lambda (dir)
- (file-system-mapping
- (source dir)
- (target dir)
- (writable? #f)))
- reqs)))
+ (override-user-mappings
+ user home
+ (append user-mappings
+ ;; Current working directory.
+ (list (file-system-mapping
+ (source cwd)
+ (target cwd)
+ (writable? #t)))
+ ;; When in Rome, do as Nix build.cc does: Automagically
+ ;; map common network configuration files.
+ (if network?
+ %network-file-mappings
+ '())
+ ;; Mappings for the union closure of all inputs.
+ (map (lambda (dir)
+ (file-system-mapping
+ (source dir)
+ (target dir)
+ (writable? #f)))
+ reqs))))
(file-systems (append %container-file-systems
(map file-system-mapping->bind-mount
mappings))))
@@ -447,8 +462,7 @@ symbolic link from ~/.guix-profile to the environment profile."
;; The same variables as in Nix's 'build.cc'.
'("TMPDIR" "TEMPDIR" "TMP" "TEMP"))
- ;; Create a dummy home directory under the same name as on the
- ;; host.
+ ;; Create a dummy home directory.
(mkdir-p home-dir)
(setenv "HOME" home-dir)
@@ -475,7 +489,7 @@ symbolic link from ~/.guix-profile to the environment profile."
;; For convenience, start in the user's current working
;; directory rather than the root directory.
- (chdir cwd)
+ (chdir (override-user-dir user home cwd))
(primitive-exit/status
;; A container's environment is already purified, so no need to
@@ -485,6 +499,60 @@ symbolic link from ~/.guix-profile to the environment profile."
(delq 'net %namespaces) ; share host network
%namespaces)))))))
+(define (mock-passwd passwd user-override shell)
+ "Generate mock information for '/etc/passwd'. If USER-OVERRIDE is not '#f',
+it is expected to be a string representing the mock username; it will produce
+a user of that name, with a home directory of '/home/USER-OVERRIDE', and no
+GECOS field. If USER-OVERRIDE is '#f', data will be inherited from PASSWD.
+In either case, the shadow password and UID/GID are cleared, since the user
+runs as root within the container. SHELL will always be used in place of the
+shell in PASSWD.
+
+The resulting vector is suitable for use with Guile's POSIX user procedures.
+
+See passwd(5) for more information each of the fields."
+ (if user-override
+ (vector
+ user-override
+ "x" "0" "0" ;; no shadow, user is now root
+ "" ;; no personal information
+ (user-override-home user-override)
+ shell)
+ (vector
+ (passwd:name passwd)
+ "x" "0" "0" ;; no shadow, user is now root
+ (passwd:gecos passwd)
+ (passwd:dir passwd)
+ shell)))
+
+(define (user-override-home user)
+ "Return home directory for override user USER."
+ (string-append "/home/" user))
+
+(define (override-user-mappings user home mappings)
+ "If a username USER is provided, rewrite each HOME prefix in file system
+mappings MAPPINGS to a home directory determined by 'override-user-dir';
+otherwise, return MAPPINGS."
+ (if (not user)
+ mappings
+ (map (lambda (mapping)
+ (let ((target (file-system-mapping-target mapping)))
+ (if (string-prefix? home target)
+ (file-system-mapping
+ (source (file-system-mapping-source mapping))
+ (target (override-user-dir user home target))
+ (writable? (file-system-mapping-writable? mapping)))
+ mapping)))
+ mappings)))
+
+(define (override-user-dir user home dir)
+ "If username USER is provided, overwrite string prefix HOME in DIR with a
+directory determined by 'user-override-home'; otherwise, return DIR."
+ (if (and user (string-prefix? home dir))
+ (string-append (user-override-home user)
+ (substring dir (string-length home)))
+ dir))
+
(define (link-environment profile home-dir)
"Create a symbolic link from HOME-DIR/.guix-profile to PROFILE."
(let ((profile-dir (string-append home-dir "/.guix-profile")))
@@ -572,6 +640,7 @@ message if any test fails."
(container? (assoc-ref opts 'container?))
(link-prof? (assoc-ref opts 'link-profile?))
(network? (assoc-ref opts 'network?))
+ (user (assoc-ref opts 'user))
(bootstrap? (assoc-ref opts 'bootstrap?))
(system (assoc-ref opts 'system))
(command (or (assoc-ref opts 'exec)
@@ -606,6 +675,8 @@ message if any test fails."
(when (and (not container?) link-prof?)
(leave (G_ "--link-prof cannot be used without --container~%")))
+ (when (and (not container?) user)
+ (leave (G_ "--user cannot be used without --container~%")))
(with-store store
(set-build-options-from-command-line store opts)
@@ -653,6 +724,7 @@ message if any test fails."
"/bin/sh"))))
(launch-environment/container #:command command
#:bash bash-binary
+ #:user user
#:user-mappings mappings
#:profile profile
#:paths paths
diff --git a/tests/guix-environment.sh b/tests/guix-environment.sh
index e995636df..a1ce96579 100644
--- a/tests/guix-environment.sh
+++ b/tests/guix-environment.sh
@@ -74,6 +74,16 @@ guix environment --bootstrap --ad-hoc guile-bootstrap --pure \
-- guile -c "$linktest"
)
+# Test that user can be mocked.
+usertest='(exit (and (string=? (getenv "HOME") "/home/foognu")
+ (string=? (passwd:name (getpwuid 0)) "foognu")
+ (file-exists? "/home/foognu/umock")))'
+touch "$tmpdir/umock"
+HOME="$tmpdir" guix environment --bootstrap --container --user=foognu \
+ --ad-hoc guile-bootstrap --pure \
+ --share="$tmpdir/umock" \
+ -- guile -c "$usertest"
+
# Make sure '-r' works as expected.
rm -f "$gcroot"
expected="`guix environment --bootstrap --ad-hoc guile-bootstrap \
--
2.15.1
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQIcBAEBCgAGBQJaaqCcAAoJEIyRe39dxRuiCZcP/0VEvCAiwTsb96+j8KGKZeDS
3pErm7SbPR5pNkipDzyp1qqyGTreWXSnLBy3N9ChXV+j35dHdZ3VhOVS6OUJMiJv
CxdG7y86naxbSidQzxMmX0eujUxOvL2xmhemVuiOuKmbHana1eI2NG216pneNaes
zwngRmALoB2M9nvNjadKgtWtyMasOJpy6ZfeW9Zze4W521WJnn8KOqAKzJbqraCN
qmAxkg80DoVLKlIW6VHetcYmN3WEw+XqV1vw8PFofiKwkIjBZlCh28AK7h5eweFi
MGXFbFTifwQg2asLDT4vpBek+DcE+4PyLid5hZMVsiAKEbLP9dHRJCbNUcSeCLQs
NWxkks/hMEg1QddReuLzgUsOhyGW2RpVWRhh7+L+YIkL18yRfW63viTdbO2+ucfa
yJGiUrmlgAk64lYiV0oDSgq36CgBlhdeUVchLgBEv9AgqMBUZtF4sc6AfnilASHM
8Flv/pcBhz0JJ0M/8YjrzlUCz6b7KnkAKycxOQgvpyq8Qu9VoOE5XN7c1qEJ4ypE
LIKbiMdoH77ySqyd8B4X/fXiqyIGaUq7Umv1ii/g4PImeqxWjrwgAVkFMAwZFIRK
CKASx4p+1+zQKONs93cYyHoTzxaNEC9TJCxnRD6Lioqos8wuGVBV0Pi7G/OFSmu7
xktWhFqAE9HRI/SJZIDs
=OLI8
-----END PGP SIGNATURE-----

L
L
Ludovic Courtès wrote on 2 Mar 2018 11:33
(name . Mike Gerwitz)(address . mtg@gnu.org)(address . 30257@debbugs.gnu.org)
87y3jahily.fsf@gnu.org
Mike Gerwitz <mtg@gnu.org> skribis:

Toggle quote (14 lines)
> This change allows overriding the home directory of all filesystem mappings to
> help hide the identity of the calling user in a container.
>
> * doc/guix.texi (Invoking guix environment)[--container]: Mention --user.
> [--user]: Add item.
> * guix/scripts/environment.scm (show-help): Add --user.
> (%options): Add --user.
> (launch-environment/container) Add 'user' parameter. Update doc. Override
> 'user-mappings' using 'override-user-mappings'. Consider override for chdir.
> (mock-passwd, user-override-home, overrid-euser-dir): New procedures.
> (guix-environment): Disallow --user without --container. Provide user to
> 'launch-environment/container'.
> * tests/guix-environment.sh: Add user test.

Awesome, I moved the test to guix-environment-container.sh and applied.

Ludo’.
L
L
Ludovic Courtès wrote on 3 Mar 2018 22:21
control message for bug #30257
(address . control@debbugs.gnu.org)
87fu5gam8c.fsf@gnu.org
tags 30257 fixed
close 30257
?
Your comment

This issue is archived.

To comment on this conversation send an email to 30257@debbugs.gnu.org

To respond to this issue using the mumi CLI, first switch to it
mumi current 30257
Then, you may apply the latest patchset in this issue (with sign off)
mumi am -- -s
Or, compose a reply to this issue
mumi compose
Or, send patches to this issue
mumi send-email *.patch