aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am3
-rw-r--r--configure.ac10
-rw-r--r--doc/guix.texi90
-rw-r--r--etc/guix-daemon.cil.in285
4 files changed, 386 insertions, 2 deletions
diff --git a/Makefile.am b/Makefile.am
index 1e4fefe3fe..eb5d38231b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -446,6 +446,9 @@ dist_zshcompletion_DATA = etc/completion/zsh/_guix
# Fish completion file.
dist_fishcompletion_DATA = etc/completion/fish/guix.fish
+# SELinux policy
+dist_selinux_policy_DATA = etc/guix-daemon.cil
+
EXTRA_DIST = \
HACKING \
ROADMAP \
diff --git a/configure.ac b/configure.ac
index f69f796484..398846f64b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -54,6 +54,13 @@ AC_ARG_WITH([fish-completion-dir],
[fishcompletiondir='${datadir}/fish/vendor_completions.d'])
AC_SUBST([fishcompletiondir])
+AC_ARG_WITH([selinux-policy-dir],
+ AC_HELP_STRING([--with-selinux-policy-dir=DIR],
+ [name of the SELinux policy directory]),
+ [selinux_policydir="$withval"],
+ [selinux_policydir='${datadir}/selinux/'])
+AC_SUBST([selinux_policydir])
+
dnl Better be verbose.
AC_MSG_CHECKING([for the store directory])
AC_MSG_RESULT([$storedir])
@@ -272,7 +279,8 @@ esac
AC_CONFIG_FILES([Makefile
po/guix/Makefile.in
po/packages/Makefile.in
- guix/config.scm])
+ etc/guix-daemon.cil
+ guix/config.scm])
AC_CONFIG_FILES([test-env:build-aux/test-env.in], [chmod +x test-env])
AC_CONFIG_FILES([pre-inst-env:build-aux/pre-inst-env.in],
diff --git a/doc/guix.texi b/doc/guix.texi
index c3b7d07d84..68f6c12294 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -21,7 +21,7 @@ Copyright @copyright{} 2015, 2016 Mathieu Lirzin@*
Copyright @copyright{} 2014 Pierre-Antoine Rault@*
Copyright @copyright{} 2015 Taylan Ulrich Bayırlı/Kammer@*
Copyright @copyright{} 2015, 2016, 2017 Leo Famulari@*
-Copyright @copyright{} 2015, 2016, 2017 Ricardo Wurmus@*
+Copyright @copyright{} 2015, 2016, 2017, 2018 Ricardo Wurmus@*
Copyright @copyright{} 2016 Ben Woodcroft@*
Copyright @copyright{} 2016, 2017 Chris Marusich@*
Copyright @copyright{} 2016, 2017 Efraim Flashner@*
@@ -123,6 +123,7 @@ Setting Up the Daemon
* Build Environment Setup:: Preparing the isolated build environment.
* Daemon Offload Setup:: Offloading builds to remote machines.
+* SELinux Support:: Using an SELinux policy for the daemon.
Package Management
@@ -754,6 +755,7 @@ the daemon to download pre-built binaries.
@menu
* Build Environment Setup:: Preparing the isolated build environment.
* Daemon Offload Setup:: Offloading builds to remote machines.
+* SELinux Support:: Using an SELinux policy for the daemon.
@end menu
@node Build Environment Setup
@@ -1081,6 +1083,92 @@ main node:
@end example
+@node SELinux Support
+@subsection SELinux Support
+
+@cindex SELinux, daemon policy
+@cindex mandatory access control, SELinux
+@cindex security, guix-daemon
+Guix includes an SELinux policy file at @file{etc/guix-daemon.cil} that
+can be installed on a system where SELinux is enabled, in order to label
+Guix files and to specify the expected behavior of the daemon. Since
+GuixSD does not provide an SELinux base policy, the daemon policy cannot
+be used on GuixSD.
+
+@subsubsection Installing the SELinux policy
+@cindex SELinux, policy installation
+To install the policy run this command as root:
+
+@example
+semodule -i etc/guix-daemon.cil
+@end example
+
+Then relabel the file system with @code{restorecon} or by a different
+mechanism provided by your system.
+
+Once the policy is installed, the file system has been relabeled, and
+the daemon has been restarted, it should be running in the
+@code{guix_daemon_t} context. You can confirm this with the following
+command:
+
+@example
+ps -Zax | grep guix-daemon
+@end example
+
+Monitor the SELinux log files as you run a command like @code{guix build
+hello} to convince yourself that SELinux permits all necessary
+operations.
+
+@subsubsection Limitations
+@cindex SELinux, limitations
+
+This policy is not perfect. Here is a list of limitations or quirks
+that should be considered when deploying the provided SELinux policy for
+the Guix daemon.
+
+@enumerate
+@item
+@code{guix_daemon_socket_t} isn’t actually used. None of the socket
+operations involve contexts that have anything to do with
+@code{guix_daemon_socket_t}. It doesn’t hurt to have this unused label,
+but it would be preferrable to define socket rules for only this label.
+
+@item
+@code{guix gc} cannot access arbitrary links to profiles. By design,
+the file label of the destination of a symlink is independent of the
+file label of the link itself. Although all profiles under
+$localstatedir are labelled, the links to these profiles inherit the
+label of the directory they are in. For links in the user’s home
+directory this will be @code{user_home_t}. But for links from the root
+user’s home directory, or @file{/tmp}, or the HTTP server’s working
+directory, etc, this won’t work. @code{guix gc} would be prevented from
+reading and following these links.
+
+@item
+The daemon’s feature to listen for TCP connections might no longer work.
+This might require extra rules, because SELinux treats network sockets
+differently from files.
+
+@item
+Currently all files with a name matching the regular expression
+@code{/gnu/store/.+-(guix-.+|profile)/bin/guix-daemon} are assigned the
+label @code{guix_daemon_exec_t}; this means that @emph{any} file with
+that name in any profile would be permitted to run in the
+@code{guix_daemon_t} domain. This is not ideal. An attacker could
+build a package that provides this executable and convince a user to
+install and run it, which lifts it into the @code{guix_daemon_t} domain.
+At that point SELinux could not prevent it from accessing files that are
+allowed for processes in that domain.
+
+We could generate a much more restrictive policy at installation time,
+so that only the @emph{exact} file name of the currently installed
+@code{guix-daemon} executable would be labelled with
+@code{guix_daemon_exec_t}, instead of using a broad regular expression.
+The downside is that root would have to install or upgrade the policy at
+installation time whenever the Guix package that provides the
+effectively running @code{guix-daemon} executable is upgraded.
+@end enumerate
+
@node Invoking guix-daemon
@section Invoking @command{guix-daemon}
diff --git a/etc/guix-daemon.cil.in b/etc/guix-daemon.cil.in
new file mode 100644
index 0000000000..c0c82d8fbb
--- /dev/null
+++ b/etc/guix-daemon.cil.in
@@ -0,0 +1,285 @@
+; -*- lisp -*-
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+;; This is a specification for SELinux 2.7 written in the SELinux Common
+;; Intermediate Language (CIL). It refers to types that must be defined in
+;; the system's base policy.
+
+(block guix_daemon
+ ;; Require existing types
+ (typeattributeset cil_gen_require init_t)
+ (typeattributeset cil_gen_require tmp_t)
+ (typeattributeset cil_gen_require nscd_var_run_t)
+ (typeattributeset cil_gen_require var_log_t)
+ (typeattributeset cil_gen_require domain)
+
+ ;; Declare own types
+ (type guix_daemon_t)
+ (roletype object_r guix_daemon_t)
+ (type guix_daemon_conf_t)
+ (roletype object_r guix_daemon_conf_t)
+ (type guix_daemon_exec_t)
+ (roletype object_r guix_daemon_exec_t)
+ (type guix_daemon_socket_t)
+ (roletype object_r guix_daemon_socket_t)
+ (type guix_store_content_t)
+ (roletype object_r guix_store_content_t)
+ (type guix_profiles_t)
+ (roletype object_r guix_profiles_t)
+
+ ;; These types are domains, thereby allowing process rules
+ (typeattributeset domain (guix_daemon_t guix_daemon_exec_t))
+
+ (level low (s0))
+
+ ;; When a process in init_t or guix_store_content_t spawns a
+ ;; guix_daemon_exec_t process, let it run in the guix_daemon_t context
+ (typetransition init_t guix_daemon_exec_t
+ process guix_daemon_t)
+ (typetransition guix_store_content_t guix_daemon_exec_t
+ process guix_daemon_t)
+
+ ;; Permit communication with NSCD
+ (allow guix_daemon_t
+ nscd_var_run_t
+ (file (map read)))
+ (allow guix_daemon_t
+ nscd_var_run_t
+ (dir (search)))
+ (allow guix_daemon_t
+ nscd_var_run_t
+ (sock_file (write)))
+ (allow guix_daemon_t
+ nscd_t
+ (fd (use)))
+ (allow guix_daemon_t
+ nscd_t
+ (unix_stream_socket (connectto)))
+
+ ;; Permit logging and temp file access
+ (allow guix_daemon_t
+ tmp_t
+ (lnk_file (setattr unlink)))
+ (allow guix_daemon_t
+ tmp_t
+ (dir (create
+ rmdir
+ add_name remove_name
+ open read write
+ getattr setattr
+ search)))
+ (allow guix_daemon_t
+ var_log_t
+ (file (create getattr open write)))
+ (allow guix_daemon_t
+ var_log_t
+ (dir (getattr write add_name)))
+ (allow guix_daemon_t
+ var_run_t
+ (lnk_file (read)))
+ (allow guix_daemon_t
+ var_run_t
+ (dir (search)))
+
+ ;; Spawning processes, execute helpers
+ (allow guix_daemon_t
+ self
+ (process (fork)))
+ (allow guix_daemon_t
+ guix_daemon_exec_t
+ (file (execute execute_no_trans read open)))
+
+ ;; TODO: unknown
+ (allow guix_daemon_t
+ root_t
+ (dir (mounton)))
+ (allow guix_daemon_t
+ fs_t
+ (filesystem (getattr)))
+ (allow guix_daemon_conf_t
+ fs_t
+ (filesystem (associate)))
+
+ ;; Build isolation
+ (allow guix_daemon_t
+ guix_store_content_t
+ (file (mounton)))
+ (allow guix_store_content_t
+ fs_t
+ (filesystem (associate)))
+ (allow guix_daemon_t
+ guix_store_content_t
+ (dir (mounton)))
+ (allow guix_daemon_t
+ guix_daemon_t
+ (capability (net_admin
+ fsetid fowner
+ chown setuid setgid
+ dac_override dac_read_search
+ sys_chroot)))
+ (allow guix_daemon_t
+ fs_t
+ (filesystem (unmount)))
+ (allow guix_daemon_t
+ devpts_t
+ (filesystem (mount)))
+ (allow guix_daemon_t
+ devpts_t
+ (chr_file (setattr getattr)))
+ (allow guix_daemon_t
+ tmpfs_t
+ (filesystem (mount)))
+ (allow guix_daemon_t
+ tmpfs_t
+ (dir (getattr)))
+ (allow guix_daemon_t
+ proc_t
+ (filesystem (mount)))
+ (allow guix_daemon_t
+ null_device_t
+ (chr_file (getattr open read write)))
+ (allow guix_daemon_t
+ kvm_device_t
+ (chr_file (getattr)))
+ (allow guix_daemon_t
+ zero_device_t
+ (chr_file (getattr)))
+ (allow guix_daemon_t
+ urandom_device_t
+ (chr_file (getattr)))
+ (allow guix_daemon_t
+ random_device_t
+ (chr_file (getattr)))
+ (allow guix_daemon_t
+ devtty_t
+ (chr_file (getattr)))
+
+ ;; Access to store items
+ (allow guix_daemon_t
+ guix_store_content_t
+ (dir (reparent
+ create
+ getattr setattr
+ search rename
+ add_name remove_name
+ open write
+ rmdir)))
+ (allow guix_daemon_t
+ guix_store_content_t
+ (file (create
+ lock
+ setattr getattr
+ execute execute_no_trans
+ link unlink
+ map
+ rename
+ open read write)))
+ (allow guix_daemon_t
+ guix_store_content_t
+ (lnk_file (create
+ getattr setattr
+ link unlink
+ read
+ rename)))
+
+ ;; Access to configuration files and directories
+ (allow guix_daemon_t
+ guix_daemon_conf_t
+ (dir (search
+ setattr getattr
+ add_name remove_name
+ open read write)))
+ (allow guix_daemon_t
+ guix_daemon_conf_t
+ (file (create
+ lock
+ map
+ getattr setattr
+ unlink
+ open read write)))
+ (allow guix_daemon_t
+ guix_daemon_conf_t
+ (lnk_file (create getattr rename unlink)))
+
+ ;; Access to profiles
+ (allow guix_daemon_t
+ guix_profiles_t
+ (dir (getattr setattr read open)))
+ (allow guix_daemon_t
+ guix_profiles_t
+ (lnk_file (read getattr)))
+
+ ;; Access to profile links in the home directory
+ ;; TODO: allow access to profile links *anywhere* on the filesystem
+ (allow guix_daemon_t
+ user_home_t
+ (lnk_file (read getattr)))
+ (allow guix_daemon_t
+ user_home_t
+ (dir (search)))
+
+ ;; Socket operations
+ (allow guix_daemon_t
+ init_t
+ (fd (use)))
+ (allow guix_daemon_t
+ init_t
+ (unix_stream_socket (write)))
+ (allow guix_daemon_t
+ guix_daemon_conf_t
+ (unix_stream_socket (listen)))
+ (allow guix_daemon_t
+ guix_daemon_conf_t
+ (sock_file (create unlink)))
+ (allow guix_daemon_t
+ self
+ (unix_stream_socket (create
+ read write
+ connect bind accept
+ getopt setopt)))
+ (allow guix_daemon_t
+ self
+ (fifo_file (write read)))
+ (allow guix_daemon_t
+ self
+ (udp_socket (ioctl create)))
+
+ ;; Label file system
+ (filecon "@guix_sysconfdir@/guix(/.*)?"
+ any (system_u object_r guix_daemon_conf_t (low low)))
+ (filecon "@guix_localstatedir@/guix(/.*)?"
+ any (system_u object_r guix_daemon_conf_t (low low)))
+ (filecon "@guix_localstatedir@/guix/profiles(/.*)?"
+ any (system_u object_r guix_profiles_t (low low)))
+ (filecon "/gnu"
+ dir (unconfined_u object_r guix_store_content_t (low low)))
+ (filecon "@storedir@(/.+)?"
+ any (unconfined_u object_r guix_store_content_t (low low)))
+ (filecon "@storedir@/[^/]+/.+"
+ any (unconfined_u object_r guix_store_content_t (low low)))
+ (filecon "@prefix@/bin/guix-daemon"
+ file (system_u object_r guix_daemon_exec_t (low low)))
+ (filecon "@storedir@/.+-(guix-.+|profile)/bin/guix-daemon"
+ file (system_u object_r guix_daemon_exec_t (low low)))
+ (filecon "@storedir@/.+-(guix-.+|profile)/libexec/guix-authenticate"
+ file (system_u object_r guix_daemon_exec_t (low low)))
+ (filecon "@storedir@/.+-(guix-.+|profile)/libexec/guix/(.*)?"
+ any (system_u object_r guix_daemon_exec_t (low low)))
+ (filecon "@guix_localstatedir@/guix/daemon-socket/socket"
+ any (system_u object_r guix_daemon_socket_t (low low))))