diff --git a/.gitignore b/.gitignore index 4f7308b295c..8779222df0c 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,7 @@ /doc/stamp-vti /doc/version.texi /doc/version-*.texi +/etc/apparmor.d/tunables/guix /etc/committer.scm /etc/gnu-store.mount /etc/guix-daemon.cil diff --git a/Makefile.am b/Makefile.am index 106849e89f0..bf7d1556f0e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -746,6 +746,13 @@ dist_fishcompletion_DATA = etc/completion/fish/guix.fish # SELinux policy nodist_selinux_policy_DATA = etc/guix-daemon.cil +# AppArmor profiles. +nodist_apparmor_profile_DATA = \ + etc/apparmor.d/guix-daemon + +nodist_apparmor_profile_tunables_DATA = \ + etc/apparmor.d/tunables/guix + EXTRA_DIST += \ .dir-locals.el \ .guix-authorizations \ diff --git a/configure.ac b/configure.ac index 82f12507745..6ad8e427736 100644 --- a/configure.ac +++ b/configure.ac @@ -81,6 +81,15 @@ AC_ARG_WITH([selinux-policy-dir], [selinux_policydir='${datadir}/selinux/']) AC_SUBST([selinux_policydir]) +AC_ARG_WITH([apparmor-profile-dir], + AS_HELP_STRING([--with-apparmor-profile-dir=DIR], + [name of the AppArmor profile directory]), + [apparmor_profiledir="$withval"], + [apparmor_profiledir='${sysconfdir}/apparmor.d']) +AC_SUBST([apparmor_profiledir]) +apparmor_profile_tunablesdir='${apparmor_profiledir}/tunables' +AC_SUBST([apparmor_profile_tunablesdir]) + dnl Better be verbose. AC_MSG_CHECKING([for the store directory]) AC_MSG_RESULT([$storedir]) @@ -308,6 +317,7 @@ AC_CONFIG_FILES([Makefile po/guix/Makefile.in po/packages/Makefile.in etc/guix-daemon.cil + etc/apparmor.d/tunables/guix guix/config.scm]) AC_CONFIG_FILES([etc/committer.scm], [chmod +x etc/committer.scm]) diff --git a/doc/guix.texi b/doc/guix.texi index d7d40ceb223..fc25b653f32 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -146,6 +146,7 @@ Copyright @copyright{} 2025 Artur Wroblewski@* Copyright @copyright{} 2025 Edouard Klein@* Copyright @copyright{} 2025 Rodion Goritskov@* Copyright @copyright{} 2025 dan@* +Copyright @copyright{} 2025 NoƩ Lopez@* Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or @@ -919,6 +920,7 @@ pre-built binaries. * 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. +* AppArmor Support:: Using an AppArmor profile for the daemon. @end menu @node Build Environment Setup @@ -1097,6 +1099,8 @@ systemctl daemon-reload systemctl start guix-daemon @end example +If your system has AppArmor enabled, @pxref{AppArmor Support}. + @quotation Warning The commands above assume that @command{guix pull} was run for the root user. You can check whether this is the case by running this command: @@ -1526,6 +1530,43 @@ installation time whenever the Guix package that provides the effectively running @code{guix-daemon} executable is upgraded. @end enumerate +@node AppArmor Support +@subsection AppArmor Support + +@cindex AppArmor + +Guix includes an AppArmor profile for the build daemon in +@file{etc/apparmor.d/guix-daemon} that can be installed on systems with +strict AppArmor policies to allow it to run unprivileged +(@pxref{Build Environment Setup}). Indeed, the unprivileged daemon makes +use of Linux user namespaces but these are disallowed +without an AppArmor policy on some systems like Ubuntu. + +To know if this applies to you, check if the +@code{kernel.apparmor_restrict_unprivileged_userns} kernel parameter is +enabled. + +@subsubsection Installing the AppArmor profile +@cindex AppArmor, profile installation + +@quotation Note +The @code{guix-install.sh} binary installation script offers to perform +the steps below for you (@pxref{Binary Installation}). +@end quotation + +Run these commands as root to install the profile: + +@example +export apparmor_sources=/var/guix/profiles/per-user/root/current-guix/etc/apparmor.d +cp -f -t /etc/apparmor.d/tunables "$apparmor_sources/tunables/guix" +cp -f -t /etc/apparmor.d "$apparmor_sources/guix-daemon" +cp -f -t /etc/apparmor.d "$apparmor_sources/guix" +apparmor_parser -r /etc/apparmor.d/guix-daemon +apparmor_parser -r /etc/apparmor.d/guix +@end example + +After this, the build daemon will be able to function correctly. + @node Invoking guix-daemon @section Invoking @command{guix-daemon} @cindex @command{guix-daemon} diff --git a/etc/apparmor.d/guix-daemon b/etc/apparmor.d/guix-daemon new file mode 100644 index 00000000000..cb1ee92685c --- /dev/null +++ b/etc/apparmor.d/guix-daemon @@ -0,0 +1,88 @@ +abi , + +include +include + +profile guix-daemon @{guix_storedir}/*-{guix-daemon,guix}-*/bin/guix-daemon flags=(enforce,attach_disconnected.path=/disconnected) { + include + + userns, + signal, + capability sys_admin, + capability net_admin, + capability sys_chroot, + capability setgid, + capability chown, + network dgram, + umount, + mount, + pivot_root, + # Paths inside build chroot + /real-root/ w, + / w, + + @{guix_localstatedir}/guix/** rwk, + /var/log/guix/** w, + owner @{PROC}/@{pid}/{fd/,environ} r, + owner @{PROC}/@{pid}/oom_score_adj w, + owner @{PROC}/@{pid}/uid_map rw, + owner @{PROC}/@{pid}/gid_map rw, + owner @{PROC}/@{pid}/setgroups w, + @{guix_storedir}/ r, + @{guix_storedir}/** rwlmk, + @{guix_storedir}/*/bin/guile cx -> guix-builder, + @{guix_storedir}/*-guix-command cx -> guix-helper, + @{guix_storedir}/*-guix-*/bin/guix cx -> guix-helper, + @{etc_rw}/nsswitch.conf r, + @{etc_rw}/passwd r, + @{etc_rw}/group r, + owner /tmp/** rwl, + owner /var/tmp/** rwl, + + /usr/bin/newgidmap Ux, + + # Site-specific additions and overrides. See local/README for details. + include if exists + + profile guix-builder flags=(enforce,attach_disconnected.path=/disconnected) { + include + + signal (receive), + + @{guix_storedir}/** rwlmkux, + + owner /tmp/** rw, + + @{PROC}/@{pid}/fd/ r, + + /disconnected/** rw, + } + + # This is for any time guix is called by the daemon as a helper: + # - guix download + # - guix discover + # - guix gc --list-busy + # - probably more? + profile guix-helper flags=(enforce,attach_disconnected.path=/disconnected) { + include + include + + signal (receive), + ptrace (read) peer=guix-daemon, + + /disconnected/run/dbus/system_bus_socket rw, + dbus (send, receive), + @{guix_localstatedir}/guix/discover/ rw, + @{guix_localstatedir}/guix/discover/* rw, + + @{guix_localstatedir}/guix/substitute/ rw, + @{guix_localstatedir}/guix/substitute/** rwk, + + @{guix_sysconfdir}/guix/** r, + + @{guix_storedir}/** rwlmix, + + @{PROC}/ r, + owner @{PROC}/@{pid}/{fd/,environ} r, + } +} diff --git a/etc/apparmor.d/tunables/guix.in b/etc/apparmor.d/tunables/guix.in new file mode 100644 index 00000000000..e93f7caeb7d --- /dev/null +++ b/etc/apparmor.d/tunables/guix.in @@ -0,0 +1,5 @@ +@{guix_storedir} = @storedir@ +@{guix_sysconfdir} = @guix_sysconfdir@ +@{guix_localstatedir} = @guix_localstatedir@ + +include if exists \ No newline at end of file diff --git a/gnu/packages/package-management.scm b/gnu/packages/package-management.scm index f04bd5efd9f..eadaea4967d 100644 --- a/gnu/packages/package-management.scm +++ b/gnu/packages/package-management.scm @@ -234,6 +234,10 @@ (string-append "--with-bash-completion-dir=" (assoc-ref %outputs "out") "/etc/bash_completion.d") + ;; TODO: Uncomment after guix is updated. + ;; (string-append "--with-apparmor-profile-dir=" + ;; (assoc-ref %outputs "out") + ;; "/etc/apparmor.d") ;; Set 'DOT_USER_PROGRAM' to the empty string so ;; we don't keep a reference to Graphviz, whose