~ruther/guix-local

b617a9fe239ea645c816d6afcb81d5476f760d84 — Ricardo Wurmus 8 years ago 5ac1143
etc: Add SELinux policy for the daemon.

* etc/guix-daemon.cil.in: New file.
* Makefile.am (dist_selinux_policy_DATA): Define it.
* configure.ac: Handle --with-selinux-policy-dir.
* doc/guix.texi (SELinux Support): New section.
4 files changed, 386 insertions(+), 2 deletions(-)

M Makefile.am
M configure.ac
M doc/guix.texi
A etc/guix-daemon.cil.in
M Makefile.am => Makefile.am +3 -0
@@ 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						\

M configure.ac => configure.ac +9 -1
@@ 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],

M doc/guix.texi => doc/guix.texi +89 -1
@@ 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}


A etc/guix-daemon.cil.in => etc/guix-daemon.cil.in +285 -0
@@ 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))))