* 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.gn-latest-20200428
@@ -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 \ | |||
@@ -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], | |||
@@ -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} | |||
@@ -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)))) |