From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:44291) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eeujK-0001fk-LW for guix-patches@gnu.org; Thu, 25 Jan 2018 22:31:08 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1eeujI-0002xx-Nb for guix-patches@gnu.org; Thu, 25 Jan 2018 22:31:06 -0500 Received: from debbugs.gnu.org ([208.118.235.43]:35112) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1eeujI-0002xo-KS for guix-patches@gnu.org; Thu, 25 Jan 2018 22:31:04 -0500 Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1eeujI-00024K-Ej for guix-patches@gnu.org; Thu, 25 Jan 2018 22:31:04 -0500 Subject: [bug#30257] [PATCH 2/3] scripts: environment: Add --user. Resent-Message-ID: Received: from eggs.gnu.org ([2001:4830:134:3::10]:44110) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eeuir-0001bz-L2 for guix-patches@gnu.org; Thu, 25 Jan 2018 22:30:39 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1eeuip-0002hN-On for guix-patches@gnu.org; Thu, 25 Jan 2018 22:30:37 -0500 Received: from fencepost.gnu.org ([2001:4830:134:3::e]:34349) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eeuip-0002h9-JY for guix-patches@gnu.org; Thu, 25 Jan 2018 22:30:35 -0500 Received: from localhost ([::1]:37483 helo=mikegerwitz-pc.gerwitz.local) by fencepost.gnu.org with esmtps (TLS1.2:DHE_RSA_AES_128_CBC_SHA1:128) (Exim 4.82) (envelope-from ) id 1eeuip-0007C2-7E for guix-patches@gnu.org; Thu, 25 Jan 2018 22:30:35 -0500 From: Mike Gerwitz In-Reply-To: Date: Thu, 25 Jan 2018 22:29:32 -0500 References: <87vag2wopo.fsf@gnu.org> Message-Id: MIME-Version: 1.0 Content-Type: multipart/signed; boundary="=-=-="; micalg=pgp-sha512; protocol="application/pgp-signature" List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-patches-bounces+kyle=kyleam.com@gnu.org Sender: "Guix-patches" To: 30257@debbugs.gnu.org --=-=-= Content-Type: text/plain Content-Transfer-Encoding: quoted-printable 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 chdi= r. (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. =2D-- doc/guix.texi | 34 ++++++++++-- guix/scripts/environment.scm | 122 ++++++++++++++++++++++++++++++++++-----= ---- tests/guix-environment.sh | 10 ++++ 3 files changed, 137 insertions(+), 29 deletions(-) diff --git a/doc/guix.texi b/doc/guix.texi index 3b6ae1ab9..8218c6637 100644 =2D-- a/doc/guix.texi +++ b/doc/guix.texi @@ -7156,10 +7156,11 @@ Attempt to build for @var{system}---e.g., @code{i68= 6-linux}. @cindex container Run @var{command} within an isolated container. The current working directory outside the container is mapped inside the container. =2DAdditionally, a dummy home directory is created that matches the current =2Duser's home directory, and @file{/etc/passwd} is configured accordingly. =2DThe spawned process runs as the current user outside the container, but =2Dhas 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. =20 @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. =20 +@item --user=3D@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=3Dfoo \ + --expose=3D$HOME/test \ + --expose=3D/tmp/target=3D$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=3D@var{source}[=3D@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 =2D-- 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=3DUSER instead of copying the name and home of the cur= rent + user into an isolated container, use the name USER + with home directory /home/USER")) (display (G_ " --share=3DSPEC 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))))) =20 =2D(define* (launch-environment/container #:key command bash user-mappings +(define* (launch-environment/container #:key command bash user user-mappin= gs 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 =2Dhost file systems to mount inside the container. LINK-PROFILE? creates a =2Dsymbolic 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 U= SER +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)) =2D (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 mappin= gs, ;; /bin/sh, the current working directory, and possibly networ= king ;; configuration files within the container. (mappings =2D (append user-mappings =2D ;; Current working directory. =2D (list (file-system-mapping =2D (source cwd) =2D (target cwd) =2D (writable? #t))) =2D ;; When in Rome, do as Nix build.cc does: Automagic= ally =2D ;; map common network configuration files. =2D (if network? =2D %network-file-mappings =2D '()) =2D ;; Mappings for the union closure of all inputs. =2D (map (lambda (dir) =2D (file-system-mapping =2D (source dir) =2D (target dir) =2D (writable? #f))) =2D 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: Automagica= lly + ;; 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 p= rofile." ;; The same variables as in Nix's 'build.cc'. '("TMPDIR" "TEMPDIR" "TMP" "TEMP")) =20 =2D ;; Create a dummy home directory under the same name as on t= he =2D ;; host. + ;; Create a dummy home directory. (mkdir-p home-dir) (setenv "HOME" home-dir) =20 @@ -475,7 +489,7 @@ symbolic link from ~/.guix-profile to the environment p= rofile." =20 ;; For convenience, start in the user's current working ;; directory rather than the root directory. =2D (chdir cwd) + (chdir (override-user-dir user home cwd)) =20 (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))))))) =20 +(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 prod= uce +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 procedure= s. + +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." =20 (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~%"))) =20 (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 =2D-- 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" ) =20 +# Test that user can be mocked. +usertest=3D'(exit (and (string=3D? (getenv "HOME") "/home/foognu") + (string=3D? (passwd:name (getpwuid 0)) "foognu") + (file-exists? "/home/foognu/umock")))' +touch "$tmpdir/umock" +HOME=3D"$tmpdir" guix environment --bootstrap --container --user=3Dfoognu \ + --ad-hoc guile-bootstrap --pure \ + --share=3D"$tmpdir/umock" \ + -- guile -c "$usertest" + # Make sure '-r' works as expected. rm -f "$gcroot" expected=3D"`guix environment --bootstrap --ad-hoc guile-bootstrap \ =2D-=20 2.15.1 --=-=-= Content-Type: application/pgp-signature; name="signature.asc" -----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----- --=-=-=--