summary refs log tree commit diff
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))))