mirror of
https://git.in.rschanz.org/ryan77627/guix.git
synced 2024-12-24 13:28:12 -05:00
environment: Add --user.
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. Signed-off-by: Ludovic Courtès <ludo@gnu.org>
This commit is contained in:
parent
07ec349229
commit
e37944d827
3 changed files with 138 additions and 29 deletions
|
@ -7286,10 +7286,11 @@ Attempt to build for @var{system}---e.g., @code{i686-linux}.
|
||||||
@cindex container
|
@cindex container
|
||||||
Run @var{command} within an isolated container. The current working
|
Run @var{command} within an isolated container. The current working
|
||||||
directory outside the container is mapped inside the container.
|
directory outside the container is mapped inside the container.
|
||||||
Additionally, a dummy home directory is created that matches the current
|
Additionally, unless overridden with @code{--user}, a dummy home
|
||||||
user's home directory, and @file{/etc/passwd} is configured accordingly.
|
directory is created that matches the current user's home directory, and
|
||||||
The spawned process runs as the current user outside the container, but
|
@file{/etc/passwd} is configured accordingly. The spawned process runs
|
||||||
has root privileges in the context of the container.
|
as the current user outside the container, but has root privileges in
|
||||||
|
the context of the container.
|
||||||
|
|
||||||
@item --network
|
@item --network
|
||||||
@itemx -N
|
@itemx -N
|
||||||
|
@ -7313,6 +7314,31 @@ example, the @code{fontconfig} package inspects
|
||||||
@code{--link-profile} allows these programs to behave as expected within
|
@code{--link-profile} allows these programs to behave as expected within
|
||||||
the environment.
|
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}]
|
@item --expose=@var{source}[=@var{target}]
|
||||||
For containers, expose the file system @var{source} from the host system
|
For containers, expose the file system @var{source} from the host system
|
||||||
as the read-only file system @var{target} within the container. If
|
as the read-only file system @var{target} within the container. If
|
||||||
|
|
|
@ -163,6 +163,10 @@ (define (show-help)
|
||||||
(display (G_ "
|
(display (G_ "
|
||||||
-P, --link-profile link environment profile to ~/.guix-profile within
|
-P, --link-profile link environment profile to ~/.guix-profile within
|
||||||
an isolated container"))
|
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_ "
|
(display (G_ "
|
||||||
--share=SPEC for containers, share writable host file system
|
--share=SPEC for containers, share writable host file system
|
||||||
according to SPEC"))
|
according to SPEC"))
|
||||||
|
@ -250,6 +254,10 @@ (define %options
|
||||||
(option '(#\P "link-profile") #f #f
|
(option '(#\P "link-profile") #f #f
|
||||||
(lambda (opt name arg result)
|
(lambda (opt name arg result)
|
||||||
(alist-cons 'link-profile? #t 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
|
(option '("share") #t #f
|
||||||
(lambda (opt name arg result)
|
(lambda (opt name arg result)
|
||||||
(alist-cons 'file-system-mapping
|
(alist-cons 'file-system-mapping
|
||||||
|
@ -410,43 +418,50 @@ (define (launch-environment/fork command inputs paths pure?)
|
||||||
(pid (match (waitpid pid)
|
(pid (match (waitpid pid)
|
||||||
((_ . status) status)))))
|
((_ . 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?)
|
profile paths link-profile? network?)
|
||||||
"Run COMMAND within a container that features the software in PROFILE.
|
"Run COMMAND within a container that features the software in PROFILE.
|
||||||
Environment variables are set according to PATHS, a list of native search
|
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
|
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.
|
store. When NETWORK?, access to the host system network is permitted.
|
||||||
USER-MAPPINGS, a list of file system mappings, contains the user-specified
|
USER-MAPPINGS, a list of file system mappings, contains the user-specified
|
||||||
host file systems to mount inside the container. LINK-PROFILE? creates a
|
host file systems to mount inside the container. If USER is not #f, each
|
||||||
symbolic link from ~/.guix-profile to the environment profile."
|
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
|
(mlet %store-monad ((reqs (inputs->requisites
|
||||||
(list (direct-store-path bash) profile))))
|
(list (direct-store-path bash) profile))))
|
||||||
(return
|
(return
|
||||||
(let* ((cwd (getcwd))
|
(let* ((cwd (getcwd))
|
||||||
(passwd (getpwuid (getuid)))
|
(home (getenv "HOME"))
|
||||||
|
(passwd (mock-passwd (getpwuid (getuid))
|
||||||
|
user
|
||||||
|
bash))
|
||||||
(home-dir (passwd:dir passwd))
|
(home-dir (passwd:dir passwd))
|
||||||
;; Bind-mount all requisite store items, user-specified mappings,
|
;; Bind-mount all requisite store items, user-specified mappings,
|
||||||
;; /bin/sh, the current working directory, and possibly networking
|
;; /bin/sh, the current working directory, and possibly networking
|
||||||
;; configuration files within the container.
|
;; configuration files within the container.
|
||||||
(mappings
|
(mappings
|
||||||
(append user-mappings
|
(override-user-mappings
|
||||||
;; Current working directory.
|
user home
|
||||||
(list (file-system-mapping
|
(append user-mappings
|
||||||
(source cwd)
|
;; Current working directory.
|
||||||
(target cwd)
|
(list (file-system-mapping
|
||||||
(writable? #t)))
|
(source cwd)
|
||||||
;; When in Rome, do as Nix build.cc does: Automagically
|
(target cwd)
|
||||||
;; map common network configuration files.
|
(writable? #t)))
|
||||||
(if network?
|
;; When in Rome, do as Nix build.cc does: Automagically
|
||||||
%network-file-mappings
|
;; map common network configuration files.
|
||||||
'())
|
(if network?
|
||||||
;; Mappings for the union closure of all inputs.
|
%network-file-mappings
|
||||||
(map (lambda (dir)
|
'())
|
||||||
(file-system-mapping
|
;; Mappings for the union closure of all inputs.
|
||||||
(source dir)
|
(map (lambda (dir)
|
||||||
(target dir)
|
(file-system-mapping
|
||||||
(writable? #f)))
|
(source dir)
|
||||||
reqs)))
|
(target dir)
|
||||||
|
(writable? #f)))
|
||||||
|
reqs))))
|
||||||
(file-systems (append %container-file-systems
|
(file-systems (append %container-file-systems
|
||||||
(map file-system-mapping->bind-mount
|
(map file-system-mapping->bind-mount
|
||||||
mappings))))
|
mappings))))
|
||||||
|
@ -467,8 +482,7 @@ (define* (launch-environment/container #:key command bash user-mappings
|
||||||
;; The same variables as in Nix's 'build.cc'.
|
;; The same variables as in Nix's 'build.cc'.
|
||||||
'("TMPDIR" "TEMPDIR" "TMP" "TEMP"))
|
'("TMPDIR" "TEMPDIR" "TMP" "TEMP"))
|
||||||
|
|
||||||
;; Create a dummy home directory under the same name as on the
|
;; Create a dummy home directory.
|
||||||
;; host.
|
|
||||||
(mkdir-p home-dir)
|
(mkdir-p home-dir)
|
||||||
(setenv "HOME" home-dir)
|
(setenv "HOME" home-dir)
|
||||||
|
|
||||||
|
@ -495,7 +509,7 @@ (define* (launch-environment/container #:key command bash user-mappings
|
||||||
|
|
||||||
;; For convenience, start in the user's current working
|
;; For convenience, start in the user's current working
|
||||||
;; directory rather than the root directory.
|
;; directory rather than the root directory.
|
||||||
(chdir cwd)
|
(chdir (override-user-dir user home cwd))
|
||||||
|
|
||||||
(primitive-exit/status
|
(primitive-exit/status
|
||||||
;; A container's environment is already purified, so no need to
|
;; A container's environment is already purified, so no need to
|
||||||
|
@ -505,6 +519,60 @@ (define* (launch-environment/container #:key command bash user-mappings
|
||||||
(delq 'net %namespaces) ; share host network
|
(delq 'net %namespaces) ; share host network
|
||||||
%namespaces)))))))
|
%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)
|
(define (link-environment profile home-dir)
|
||||||
"Create a symbolic link from HOME-DIR/.guix-profile to PROFILE."
|
"Create a symbolic link from HOME-DIR/.guix-profile to PROFILE."
|
||||||
(let ((profile-dir (string-append home-dir "/.guix-profile")))
|
(let ((profile-dir (string-append home-dir "/.guix-profile")))
|
||||||
|
@ -592,6 +660,7 @@ (define (guix-environment . args)
|
||||||
(container? (assoc-ref opts 'container?))
|
(container? (assoc-ref opts 'container?))
|
||||||
(link-prof? (assoc-ref opts 'link-profile?))
|
(link-prof? (assoc-ref opts 'link-profile?))
|
||||||
(network? (assoc-ref opts 'network?))
|
(network? (assoc-ref opts 'network?))
|
||||||
|
(user (assoc-ref opts 'user))
|
||||||
(bootstrap? (assoc-ref opts 'bootstrap?))
|
(bootstrap? (assoc-ref opts 'bootstrap?))
|
||||||
(system (assoc-ref opts 'system))
|
(system (assoc-ref opts 'system))
|
||||||
(command (or (assoc-ref opts 'exec)
|
(command (or (assoc-ref opts 'exec)
|
||||||
|
@ -626,6 +695,8 @@ (define (guix-environment . args)
|
||||||
|
|
||||||
(when (and (not container?) link-prof?)
|
(when (and (not container?) link-prof?)
|
||||||
(leave (G_ "'--link-profile' cannot be used without '--container'~%")))
|
(leave (G_ "'--link-profile' cannot be used without '--container'~%")))
|
||||||
|
(when (and (not container?) user)
|
||||||
|
(leave (G_ "'--user' cannot be used without '--container'~%")))
|
||||||
|
|
||||||
(with-store store
|
(with-store store
|
||||||
(set-build-options-from-command-line store opts)
|
(set-build-options-from-command-line store opts)
|
||||||
|
@ -673,6 +744,7 @@ (define (guix-environment . args)
|
||||||
"/bin/sh"))))
|
"/bin/sh"))))
|
||||||
(launch-environment/container #:command command
|
(launch-environment/container #:command command
|
||||||
#:bash bash-binary
|
#:bash bash-binary
|
||||||
|
#:user user
|
||||||
#:user-mappings mappings
|
#:user-mappings mappings
|
||||||
#:profile profile
|
#:profile profile
|
||||||
#:paths paths
|
#:paths paths
|
||||||
|
|
|
@ -109,6 +109,17 @@ rm $tmpdir/mounts
|
||||||
-- guile -c "$linktest"
|
-- 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"
|
||||||
|
|
||||||
|
|
||||||
# Check the exit code.
|
# Check the exit code.
|
||||||
|
|
||||||
abnormal_exit_code="
|
abnormal_exit_code="
|
||||||
|
|
Loading…
Reference in a new issue