diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix
-index b166e7bf01f..c059c2bb002 100644
+index 6270ac778ae..57f3dda64cd 100644
--- a/maintainers/maintainer-list.nix
+++ b/maintainers/maintainer-list.nix
-@@ -4657,7 +4657,7 @@
+@@ -4779,7 +4779,7 @@
name = "Julien Dehos";
};
julm = {
githubId = 21160136;
name = "Julien Moutinho";
diff --git a/nixos/doc/manual/release-notes/rl-2105.xml b/nixos/doc/manual/release-notes/rl-2105.xml
-index 6dd14d6051e..f485eb114cc 100644
+index b7947293c01..8abee64734d 100644
--- a/nixos/doc/manual/release-notes/rl-2105.xml
+++ b/nixos/doc/manual/release-notes/rl-2105.xml
-@@ -662,6 +662,24 @@ self: super:
+@@ -795,6 +795,23 @@ environment.systemPackages = [
The option's description was incorrect regarding ownership management and has been simplified greatly.
</para>
</listitem>
+ to provide a way to disable a profile
+ and to select whether to confine in enforce mode (default)
+ or in complain mode (see <literal>journalctl -b --grep apparmor</literal>).
-+ Before enabling this module, either directly
-+ or by importing <literal><nixpkgs/nixos/modules/profiles/hardened.nix></literal>,
-+ please be sure to read the documentation of <link linkend="opt-security.apparmor.enable">security.apparmor.enable</link>,
-+ and especially the part about <xref linkend="opt-security.apparmor.killUnconfinedConfinables"/>.
++ Security-minded users may also want to enable <xref linkend="opt-security.apparmor.killUnconfinedConfinables"/>,
++ at the cost of having some of their processes killed
++ when updating to a NixOS version introducing new AppArmor profiles.
+ </para>
+ </listitem>
<listitem>
};
}
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
-index 3055459e781..d9cc86fcd4e 100644
+index 4a63a09ab84..43f052d150e 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
-@@ -199,7 +199,6 @@
+@@ -201,7 +201,6 @@
./rename.nix
./security/acme.nix
./security/apparmor.nix
./security/audit.nix
./security/auditd.nix
./security/ca.nix
+diff --git a/nixos/modules/profiles/hardened.nix b/nixos/modules/profiles/hardened.nix
+index 00aafc6831b..3f8f78f012a 100644
+--- a/nixos/modules/profiles/hardened.nix
++++ b/nixos/modules/profiles/hardened.nix
+@@ -36,6 +36,7 @@ with lib;
+ security.virtualisation.flushL1DataCache = mkDefault "always";
+
+ security.apparmor.enable = mkDefault true;
++ security.apparmor.killUnconfinedConfinables = mkDefault true;
+
+ boot.kernelParams = [
+ # Slab/slub sanity checks, redzoning, and poisoning
diff --git a/nixos/modules/security/apparmor-suid.nix b/nixos/modules/security/apparmor-suid.nix
deleted file mode 100644
index 6c479e070e2..00000000000
-
-}
diff --git a/nixos/modules/security/apparmor.nix b/nixos/modules/security/apparmor.nix
-index cfc65b347bc..dfa695b81bb 100644
+index cfc65b347bc..8683d2b487c 100644
--- a/nixos/modules/security/apparmor.nix
+++ b/nixos/modules/security/apparmor.nix
-@@ -1,59 +1,207 @@
+@@ -1,59 +1,214 @@
{ config, lib, pkgs, ... }:
++with lib;
++
let
- inherit (lib) mkIf mkOption types concatMapStrings;
+ inherit (builtins) attrNames head map match readFile;
+ inherit (lib) types;
+ inherit (config.environment) etc;
cfg = config.security.apparmor;
-+ mkDisableOption = name: lib.mkEnableOption name // {
++ mkDisableOption = name: mkEnableOption name // {
+ default = true;
+ example = false;
+ };
-+ enabledPolicies = lib.filterAttrs (n: p: p.enable) cfg.policies;
++ enabledPolicies = filterAttrs (n: p: p.enable) cfg.policies;
in
{
- };
- };
+ imports = [
-+ (lib.mkRenamedOptionModule [ "security" "virtualization" "flushL1DataCache" ] [ "security" "virtualisation" "flushL1DataCache" ])
-+ (lib.mkRemovedOptionModule [ "security" "apparmor" "confineSUIDApplications" ] "Please use the new options: `security.apparmor.policies.<policy>.enable'.")
-+ (lib.mkRemovedOptionModule [ "security" "apparmor" "profiles" ] "Please use the new option: `security.apparmor.policies'.")
++ (mkRemovedOptionModule [ "security" "apparmor" "confineSUIDApplications" ] "Please use the new options: `security.apparmor.policies.<policy>.enable'.")
++ (mkRemovedOptionModule [ "security" "apparmor" "profiles" ] "Please use the new option: `security.apparmor.policies'.")
+ apparmor/includes.nix
+ apparmor/profiles.nix
+ ];
- environment.systemPackages = [ pkgs.apparmor-utils ];
+ options = {
+ security.apparmor = {
-+ enable = lib.mkEnableOption ''the AppArmor Mandatory Access Control system.
++ enable = mkEnableOption ''
++ the AppArmor Mandatory Access Control system.
- boot.kernelParams = [ "apparmor=1" "security=apparmor" ];
+ If you're enabling this module on a running system,
- };
- };
- };
-+ Also, beware that enabling this module will by default
-+ try to kill unconfined but confinable running processes,
-+ in order to obtain a confinement matching what is declared in the NixOS configuration.
++ Also, beware that enabling this module privileges stability over security
++ by not trying to kill unconfined but newly confinable running processes by default,
++ which can happen because AppArmor can only confine new
++ or already confined processes of an executable.
+ This will happen when upgrading to a NixOS revision
+ introducing an AppArmor profile for the executable of a running process.
-+ This is because enabling an AppArmor profile for an executable
-+ can only confine new or already confined processes of that executable,
-+ but leaves already running processes unconfined.
-+ Set <link linkend="opt-security.apparmor.killUnconfinedConfinables">killUnconfinedConfinables</link>
-+ to <literal>false</literal> if you prefer to leave those processes running'';
-+ policies = lib.mkOption {
++
++ Enable <xref linkend="opt-security.apparmor.killUnconfinedConfinables"/>
++ if you want this service to send a </literal>SIGTERM</literal> to those running processes'';
++ policies = mkOption {
+ description = ''
+ AppArmor policies.
+ '';
+ options = {
+ enable = mkDisableOption "loading of the profile into the kernel";
+ enforce = mkDisableOption "enforcing of the policy or only complain in the logs";
-+ profile = lib.mkOption {
++ profile = mkOption {
+ description = "The policy of the profile.";
+ type = types.lines;
+ apply = pkgs.writeText name;
+ }));
+ default = {};
+ };
-+ includes = lib.mkOption {
++ includes = mkOption {
+ type = types.attrsOf types.lines;
+ default = {};
+ description = ''
+ List of paths to be added to AppArmor's searched paths
+ when resolving <literal>include</literal> directives.
+ '';
-+ apply = lib.mapAttrs pkgs.writeText;
++ apply = mapAttrs pkgs.writeText;
+ };
-+ packages = lib.mkOption {
++ packages = mkOption {
+ type = types.listOf types.package;
+ default = [];
+ description = "List of packages to be added to AppArmor's include path";
+ };
-+ enableCache = lib.mkEnableOption ''caching of AppArmor policies
++ enableCache = mkEnableOption ''
++ caching of AppArmor policies
+ in <literal>/var/cache/apparmor/</literal>.
+
+ Beware that AppArmor policies almost always contain Nix store paths,
+ and thus produce at each change of these paths
+ a new cached version accumulating in the cache'';
-+ killUnconfinedConfinables = mkDisableOption ''killing of processes
-+ which have an AppArmor profile enabled
-+ (in <link linkend="opt-security.apparmor.policies">policies</link>)
++ killUnconfinedConfinables = mkEnableOption ''
++ killing of processes which have an AppArmor profile enabled
++ (in <xref linkend="opt-security.apparmor.policies"/>)
+ but are not confined (because AppArmor can only confine new processes).
++
++ This is only sending a gracious <literal>SIGTERM</literal> signal to the processes,
++ not a <literal>SIGKILL</literal>.
++
+ Beware that due to a current limitation of AppArmor,
+ only profiles with exact paths (and no name) can enable such kills'';
+ };
+ };
+
-+ config = lib.mkIf cfg.enable {
++ config = mkIf cfg.enable {
+ assertions = map (policy:
+ { assertion = match ".*/.*" policy == null;
+ message = "`security.apparmor.policies.\"${policy}\"' must not contain a slash.";
+ environment.etc."apparmor.d".source = pkgs.linkFarm "apparmor.d" (
+ # It's important to put only enabledPolicies here and not all cfg.policies
+ # because aa-remove-unknown reads profiles from all /etc/apparmor.d/*
-+ lib.mapAttrsToList (name: p: {inherit name; path=p.profile;}) enabledPolicies ++
-+ lib.mapAttrsToList (name: path: {inherit name path;}) cfg.includes
++ mapAttrsToList (name: p: { inherit name; path = p.profile; }) enabledPolicies ++
++ mapAttrsToList (name: path: { inherit name path; }) cfg.includes
+ );
+ environment.etc."apparmor/parser.conf".text = ''
-+ ${if cfg.enableCache then "write-cache" else "skip-cache"}
-+ cache-loc /var/cache/apparmor
-+ Include /etc/apparmor.d
-+ '' +
-+ lib.concatMapStrings (p: "Include ${p}/etc/apparmor.d\n") cfg.packages;
++ ${if cfg.enableCache then "write-cache" else "skip-cache"}
++ cache-loc /var/cache/apparmor
++ Include /etc/apparmor.d
++ '' +
++ concatMapStrings (p: "Include ${p}/etc/apparmor.d\n") cfg.packages;
+ # For aa-logprof
+ environment.etc."apparmor/apparmor.conf".text = ''
+ '';
+ # 3 - force all perms on the rule to be user
+ default_owner_prompt = 1
+
-+ custom_includes = /etc/apparmor.d ${lib.concatMapStringsSep " " (p: "${p}/etc/apparmor.d") cfg.packages}
++ custom_includes = /etc/apparmor.d ${concatMapStringsSep " " (p: "${p}/etc/apparmor.d") cfg.packages}
+
+ [qualifiers]
+ ${pkgs.runtimeShell} = icnu
+ ${pkgs.bashInteractive}/bin/sh = icnu
+ ${pkgs.bashInteractive}/bin/bash = icnu
++ ${config.users.defaultUserShell} = icnu
+ '';
+ footer = "${pkgs.apparmor-utils}/etc/apparmor/logprof.conf";
+ passAsFile = [ "header" ];
+ xargs --verbose --no-run-if-empty --delimiter='\n' \
+ kill
+ '';
-+ commonOpts = p: "--verbose --show-cache ${lib.optionalString (!p.enforce) "--complain "}${p.profile}";
++ commonOpts = p: "--verbose --show-cache ${optionalString (!p.enforce) "--complain "}${p.profile}";
+ in {
+ Type = "oneshot";
+ RemainAfterExit = "yes";
+ ExecStartPre = "${pkgs.apparmor-utils}/bin/aa-teardown";
-+ ExecStart = lib.mapAttrsToList (n: p: "${pkgs.apparmor-parser}/bin/apparmor_parser --add ${commonOpts p}") enabledPolicies;
-+ ExecStartPost = lib.optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
++ ExecStart = mapAttrsToList (n: p: "${pkgs.apparmor-parser}/bin/apparmor_parser --add ${commonOpts p}") enabledPolicies;
++ ExecStartPost = optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
+ ExecReload =
+ # Add or replace into the kernel profiles in enabledPolicies
+ # (because AppArmor can do that without stopping the processes already confined).
-+ lib.mapAttrsToList (n: p: "${pkgs.apparmor-parser}/bin/apparmor_parser --replace ${commonOpts p}") enabledPolicies ++
++ mapAttrsToList (n: p: "${pkgs.apparmor-parser}/bin/apparmor_parser --replace ${commonOpts p}") enabledPolicies ++
+ # Remove from the kernel any profile whose name is not
+ # one of the names within the content of the profiles in enabledPolicies
+ # (indirectly read from /etc/apparmor.d/*, without recursing into sub-directory).
+ [ "${pkgs.apparmor-utils}/bin/aa-remove-unknown" ] ++
+ # Optionaly kill the processes which are unconfined but now have a profile loaded
+ # (because AppArmor can only start to confine new processes).
-+ lib.optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
++ optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
+ ExecStop = "${pkgs.apparmor-utils}/bin/aa-teardown";
+ CacheDirectory = [ "apparmor" "apparmor/logprof" ];
+ CacheDirectoryMode = "0700";
+ };
+ };
+
-+ meta.maintainers = with lib.maintainers; [ julm ];
++ meta.maintainers = with maintainers; [ julm ];
}
diff --git a/nixos/modules/security/apparmor/includes.nix b/nixos/modules/security/apparmor/includes.nix
new file mode 100644
-index 00000000000..498d7e77650
+index 00000000000..e3dd410b3bb
--- /dev/null
+++ b/nixos/modules/security/apparmor/includes.nix
-@@ -0,0 +1,301 @@
+@@ -0,0 +1,317 @@
+{ config, lib, pkgs, ... }:
+let
+ inherit (builtins) attrNames hasAttr isAttrs;
+ inherit (lib) getLib;
+ inherit (config.environment) etc;
++ # Utility to generate an AppArmor rule
++ # only when the given path exists in config.environment.etc
+ etcRule = arg:
-+ let go = {path ? null, mode ? "r", trail ? ""}:
++ let go = { path ? null, mode ? "r", trail ? "" }:
+ lib.optionalString (hasAttr path etc)
+ "${mode} ${config.environment.etc.${path}.source}${trail},";
+ in if isAttrs arg
+ then go arg
-+ else go {path=arg;};
++ else go { path = arg; };
+in
+{
+# FIXME: most of the etcRule calls below have been
+ '';
+ "abstractions/audio" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/audio"
-+ ${etcRule "asound.conf"}
-+ ${etcRule "esound/esd.conf"}
-+ ${etcRule "libao.conf"}
-+ ${etcRule {path="pulse"; trail="/";}}
-+ ${etcRule {path="pulse"; trail="/**";}}
-+ ${etcRule {path="sound"; trail="/";}}
-+ ${etcRule {path="sound"; trail="/**";}}
-+ ${etcRule {path="alsa/conf.d"; trail="/";}}
-+ ${etcRule {path="alsa/conf.d"; trail="/*";}}
-+ ${etcRule "openal/alsoft.conf"}
-+ ${etcRule "wildmidi/wildmidi.conf"}
-+ '';
++ '' + lib.concatMapStringsSep "\n" etcRule [
++ "asound.conf"
++ "esound/esd.conf"
++ "libao.conf"
++ { path = "pulse"; trail = "/"; }
++ { path = "pulse"; trail = "/**"; }
++ { path = "sound"; trail = "/"; }
++ { path = "sound"; trail = "/**"; }
++ { path = "alsa/conf.d"; trail = "/"; }
++ { path = "alsa/conf.d"; trail = "/*"; }
++ "openal/alsoft.conf"
++ "wildmidi/wildmidi.conf"
++ ];
+ "abstractions/authentication" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/authentication"
+ # Defined in security.pam
+ include <abstractions/pam>
-+ ${etcRule "nologin"}
-+ ${etcRule "securetty"}
-+ ${etcRule {path="security"; trail="/*";}}
-+ ${etcRule "shadow"}
-+ ${etcRule "gshadow"}
-+ ${etcRule "pwdb.conf"}
-+ ${etcRule "default/passwd"}
-+ ${etcRule "login.defs"}
-+ '';
++ '' + lib.concatMapStringsSep "\n" etcRule [
++ "nologin"
++ "securetty"
++ { path = "security"; trail = "/*"; }
++ "shadow"
++ "gshadow"
++ "pwdb.conf"
++ "default/passwd"
++ "login.defs"
++ ];
+ "abstractions/base" = ''
-+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/base"
-+ r ${pkgs.stdenv.cc.libc}/share/locale/**,
-+ r ${pkgs.stdenv.cc.libc}/share/locale.alias,
-+ ${lib.optionalString (pkgs.glibcLocales != null) "r ${pkgs.glibcLocales}/lib/locale/locale-archive,"}
-+ ${etcRule "localtime"}
-+ r ${pkgs.tzdata}/share/zoneinfo/**,
-+ r ${pkgs.stdenv.cc.libc}/share/i18n/**,
++ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/base"
++ r ${pkgs.stdenv.cc.libc}/share/locale/**,
++ r ${pkgs.stdenv.cc.libc}/share/locale.alias,
++ ${lib.optionalString (pkgs.glibcLocales != null) "r ${pkgs.glibcLocales}/lib/locale/locale-archive,"}
++ ${etcRule "localtime"}
++ r ${pkgs.tzdata}/share/zoneinfo/**,
++ r ${pkgs.stdenv.cc.libc}/share/i18n/**,
+ '';
+ "abstractions/bash" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/bash"
-+ # system-wide bash configuration
-+ ${etcRule "profile.dos"}
-+ ${etcRule "profile"}
-+ ${etcRule "profile.d"}
-+ ${etcRule {path="profile.d"; trail="/*";}}
-+ ${etcRule "bashrc"}
-+ ${etcRule "bash.bashrc"}
-+ ${etcRule "bash.bashrc.local"}
-+ ${etcRule "bash_completion"}
-+ ${etcRule "bash_completion.d"}
-+ ${etcRule {path="bash_completion.d"; trail="/*";}}
-+ # bash relies on system-wide readline configuration
-+ ${etcRule "inputrc"}
++
+ # bash inspects filesystems at startup
+ # and /etc/mtab is linked to /proc/mounts
+ @{PROC}/mounts
+
-+ # run out of /etc/bash.bashrc
-+ ${etcRule "DIR_COLORS"}
++ # system-wide bash configuration
++ '' + lib.concatMapStringsSep "\n" etcRule [
++ "profile.dos"
++ "profile"
++ "profile.d"
++ { path = "profile.d"; trail = "/*"; }
++ "bashrc"
++ "bash.bashrc"
++ "bash.bashrc.local"
++ "bash_completion"
++ "bash_completion.d"
++ { path = "bash_completion.d"; trail = "/*"; }
++ # bash relies on system-wide readline configuration
++ "inputrc"
++ # run out of /etc/bash.bashrc
++ "DIR_COLORS"
++ ];
++ "abstractions/consoles" = ''
++ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/consoles"
+ '';
+ "abstractions/cups-client" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/cpus-client"
+ ${etcRule "cups/cups-client.conf"}
+ '';
-+ "abstractions/consoles" = ''
-+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/consoles"
-+ '';
+ "abstractions/dbus-session-strict" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/dbus-session-strict"
+ ${etcRule "machine-id"}
+ '';
+ "abstractions/dconf" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/dconf"
-+ ${etcRule {path="dconf"; trail="/**";}}
++ ${etcRule { path = "dconf"; trail = "/**"; }}
+ '';
+ "abstractions/dri-common" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/dri-common"
+ # those are therefore added there to this "abstractions/fonts".
+ "abstractions/fonts" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/fonts"
-+ ${etcRule {path="fonts"; trail="/**";}}
++ ${etcRule { path = "fonts"; trail = "/**"; }}
+ '';
+ "abstractions/gnome" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/gnome"
-+ ${etcRule {path="gnome"; trail="/gtkrc*";}}
-+ ${etcRule {path="gtk"; trail="/*";}}
-+ ${etcRule {path="gtk-2.0"; trail="/*";}}
-+ ${etcRule {path="gtk-3.0"; trail="/*";}}
-+ ${etcRule "orbitrc"}
+ include <abstractions/fonts>
-+ ${etcRule {path="pango"; trail="/*";}}
-+ ${etcRule {path="/etc/gnome-vfs-2.0"; trail="/modules/";}}
-+ ${etcRule {path="/etc/gnome-vfs-2.0"; trail="/modules/*";}}
-+ ${etcRule "papersize"}
-+ ${etcRule {path="cups"; trail="/lpoptions";}}
-+ ${etcRule {path="gnome"; trail="/defaults.list";}}
-+ ${etcRule {path="xdg"; trail="/{,*-}mimeapps.list";}}
-+ ${etcRule "xdg/mimeapps.list"}
-+ '';
++ '' + lib.concatMapStringsSep "\n" etcRule [
++ { path = "gnome"; trail = "/gtkrc*"; }
++ { path = "gtk"; trail = "/*"; }
++ { path = "gtk-2.0"; trail = "/*"; }
++ { path = "gtk-3.0"; trail = "/*"; }
++ "orbitrc"
++ { path = "pango"; trail = "/*"; }
++ { path = "/etc/gnome-vfs-2.0"; trail = "/modules/"; }
++ { path = "/etc/gnome-vfs-2.0"; trail = "/modules/*"; }
++ "papersize"
++ { path = "cups"; trail = "/lpoptions"; }
++ { path = "gnome"; trail = "/defaults.list"; }
++ { path = "xdg"; trail = "/{,*-}mimeapps.list"; }
++ "xdg/mimeapps.list"
++ ];
+ "abstractions/kde" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/kde"
-+ ${etcRule {path="qt3"; trail="/kstylerc";}}
-+ ${etcRule {path="qt3"; trail="/qt_plugins_3.3rc";}}
-+ ${etcRule {path="qt3"; trail="/qtrc";}}
-+ ${etcRule "kderc"}
-+ ${etcRule {path="kde3"; trail="/*";}}
-+ ${etcRule "kde4rc"}
-+ ${etcRule {path="xdg"; trail="/kdeglobals";}}
-+ ${etcRule {path="xdg"; trail="/Trolltech.conf";}}
-+ '';
++ '' + lib.concatMapStringsSep "\n" etcRule [
++ { path = "qt3"; trail = "/kstylerc"; }
++ { path = "qt3"; trail = "/qt_plugins_3.3rc"; }
++ { path = "qt3"; trail = "/qtrc"; }
++ "kderc"
++ { path = "kde3"; trail = "/*"; }
++ "kde4rc"
++ { path = "xdg"; trail = "/kdeglobals"; }
++ { path = "xdg"; trail = "/Trolltech.conf"; }
++ ];
+ "abstractions/kerberosclient" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/kerberosclient"
-+ ${etcRule {path="krb5.keytab"; mode="rk";}}
-+ ${etcRule "krb5.conf"}
-+ ${etcRule "krb5.conf.d"}
-+ ${etcRule {path="krb5.conf.d"; trail="/*";}}
++ '' + lib.concatMapStringsSep "\n" etcRule [
++ { path = "krb5.keytab"; mode="rk"; }
++ "krb5.conf"
++ "krb5.conf.d"
++ { path = "krb5.conf.d"; trail = "/*"; }
+
+ # config files found via strings on libs
-+ ${etcRule "krb.conf"}
-+ ${etcRule "krb.realms"}
-+ ${etcRule "srvtab"}
-+ '';
++ "krb.conf"
++ "krb.realms"
++ "srvtab"
++ ];
+ "abstractions/ldapclient" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/ldapclient"
-+ ${etcRule "ldap.conf"}
-+ ${etcRule "ldap.secret"}
-+ ${etcRule {path="openldap"; trail="/*";}}
-+ ${etcRule {path="openldap"; trail="/cacerts/*";}}
-+ ${etcRule {path="sasl2"; trail="/*";}}
-+ '';
++ '' + lib.concatMapStringsSep "\n" etcRule [
++ "ldap.conf"
++ "ldap.secret"
++ { path = "openldap"; trail = "/*"; }
++ { path = "openldap"; trail = "/cacerts/*"; }
++ { path = "sasl2"; trail = "/*"; }
++ ];
+ "abstractions/likewise" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/likewise"
+ '';
+ "abstractions/mdns" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/mdns"
-+ ${etcRule "nss_mdns.conf"}
++ ${etcRule "nss_mdns.conf"}
+ '';
+ "abstractions/nameservice" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nameservice"
+ # looking up users by name or id, groups by name or id, hosts by name
+ # or IP, etc. These operations may be performed through files, dns,
+ # NIS, NIS+, LDAP, hesiod, wins, etc. Allow them all here.
-+ ${etcRule "group"}
-+ ${etcRule "host.conf"}
-+ ${etcRule "hosts"}
-+ ${etcRule "nsswitch.conf"}
-+ ${etcRule "gai.conf"}
-+ ${etcRule "passwd"}
-+ ${etcRule "protocols"}
-+
-+ # libtirpc (used for NIS/YP login) needs this
-+ ${etcRule "netconfig"}
++ mr ${getLib pkgs.nss}/lib/libnss_*.so*,
++ mr ${getLib pkgs.nss}/lib64/libnss_*.so*,
++ '' + lib.concatMapStringsSep "\n" etcRule [
++ "group"
++ "host.conf"
++ "hosts"
++ "nsswitch.conf"
++ "gai.conf"
++ "passwd"
++ "protocols"
+
-+ ${etcRule "resolv.conf"}
++ # libtirpc (used for NIS/YP login) needs this
++ "netconfig"
+
-+ ${etcRule {path="samba"; trail="/lmhosts";}}
-+ ${etcRule "services"}
++ "resolv.conf"
+
-+ ${etcRule "default/nss"}
++ { path = "samba"; trail = "/lmhosts"; }
++ "services"
+
-+ # libnl-3-200 via libnss-gw-name
-+ ${etcRule {path="libnl"; trail="/classid";}}
-+ ${etcRule {path="libnl-3"; trail="/classid";}}
++ "default/nss"
+
-+ mr ${getLib pkgs.nss}/lib/libnss_*.so*,
-+ mr ${getLib pkgs.nss}/lib64/libnss_*.so*,
-+ '';
++ # libnl-3-200 via libnss-gw-name
++ { path = "libnl"; trail = "/classid"; }
++ { path = "libnl-3"; trail = "/classid"; }
++ ];
+ "abstractions/nis" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nis"
+ '';
+ '';
+ "abstractions/opencl-common" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/opencl-common"
-+ ${etcRule {path="OpenCL"; trail="/**";}}
++ ${etcRule { path = "OpenCL"; trail = "/**"; }}
+ '';
+ "abstractions/opencl-mesa" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/opencl-mesa"
+ '';
+ "abstractions/openssl" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/openssl"
-+ ${etcRule {path="ssl"; trail="/openssl.cnf";}}
++ ${etcRule { path = "ssl"; trail = "/openssl.cnf"; }}
+ '';
+ "abstractions/p11-kit" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/p11-kit"
-+ ${etcRule {path="pkcs11"; trail="/";}}
-+ ${etcRule {path="pkcs11"; trail="/pkcs11.conf";}}
-+ ${etcRule {path="pkcs11"; trail="/modules/";}}
-+ ${etcRule {path="pkcs11"; trail="/modules/*";}}
-+ '';
++ '' + lib.concatMapStringsSep "\n" etcRule [
++ { path = "pkcs11"; trail = "/"; }
++ { path = "pkcs11"; trail = "/pkcs11.conf"; }
++ { path = "pkcs11"; trail = "/modules/"; }
++ { path = "pkcs11"; trail = "/modules/*"; }
++ ];
+ "abstractions/perl" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/perl"
-+ ${etcRule {path="perl"; trail="/**";}}
++ ${etcRule { path = "perl"; trail = "/**"; }}
+ '';
+ "abstractions/php" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/php"
-+ ${etcRule {path="php"; trail="/**/";}}
-+ ${etcRule {path="php5"; trail="/**/";}}
-+ ${etcRule {path="php7"; trail="/**/";}}
-+ ${etcRule {path="php"; trail="/**.ini";}}
-+ ${etcRule {path="php5"; trail="/**.ini";}}
-+ ${etcRule {path="php7"; trail="/**.ini";}}
-+ '';
++ '' + lib.concatMapStringsSep "\n" etcRule [
++ { path = "php"; trail = "/**/"; }
++ { path = "php5"; trail = "/**/"; }
++ { path = "php7"; trail = "/**/"; }
++ { path = "php"; trail = "/**.ini"; }
++ { path = "php5"; trail = "/**.ini"; }
++ { path = "php7"; trail = "/**.ini"; }
++ ];
+ "abstractions/postfix-common" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/postfix-common"
-+ ${etcRule "mailname"}
-+ ${etcRule {path="postfix"; trail="/*.cf";}}
-+ ${etcRule "postfix/main.cf"}
-+ ${etcRule "postfix/master.cf"}
-+ '';
++ '' + lib.concatMapStringsSep "\n" etcRule [
++ "mailname"
++ { path = "postfix"; trail = "/*.cf"; }
++ "postfix/main.cf"
++ "postfix/master.cf"
++ ];
+ "abstractions/python" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/python"
+ '';
+ "abstractions/qt5" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/qt5"
-+ ${etcRule {path="xdg"; trail="/QtProject/qtlogging.ini";}}
-+ ${etcRule {path="xdg/QtProject"; trail="/qtlogging.ini";}}
-+ ${etcRule "xdg/QtProject/qtlogging.ini"}
-+ '';
++ '' + lib.concatMapStringsSep "\n" etcRule [
++ { path = "xdg"; trail = "/QtProject/qtlogging.ini"; }
++ { path = "xdg/QtProject"; trail = "/qtlogging.ini"; }
++ "xdg/QtProject/qtlogging.ini"
++ ];
+ "abstractions/samba" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/samba"
-+ ${etcRule {path="samba"; trail="/*";}}
++ ${etcRule { path = "samba"; trail = "/*"; }}
+ '';
+ "abstractions/ssl_certs" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/ssl_certs"
-+ ${etcRule "ssl/certs/ca-certificates.crt"}
-+ ${etcRule "ssl/certs/ca-bundle.crt"}
-+ ${etcRule "pki/tls/certs/ca-bundle.crt"}
-+
-+ ${etcRule {path="ssl/trust"; trail="/";}}
-+ ${etcRule {path="ssl/trust"; trail="/*";}}
-+ ${etcRule {path="ssl/trust/anchors"; trail="/";}}
-+ ${etcRule {path="ssl/trust/anchors"; trail="/**";}}
-+ ${etcRule {path="pki/trust"; trail="/";}}
-+ ${etcRule {path="pki/trust"; trail="/*";}}
-+ ${etcRule {path="pki/trust/anchors"; trail="/";}}
-+ ${etcRule {path="pki/trust/anchors"; trail="/**";}}
+
-+ # security.acme NixOS module
++ # For the NixOS module: security.acme
+ r /var/lib/acme/*/cert.pem,
+ r /var/lib/acme/*/chain.pem,
+ r /var/lib/acme/*/fullchain.pem,
-+ '';
++
++ '' + lib.concatMapStringsSep "\n" etcRule [
++ "ssl/certs/ca-certificates.crt"
++ "ssl/certs/ca-bundle.crt"
++ "pki/tls/certs/ca-bundle.crt"
++
++ { path = "ssl/trust"; trail = "/"; }
++ { path = "ssl/trust"; trail = "/*"; }
++ { path = "ssl/trust/anchors"; trail = "/"; }
++ { path = "ssl/trust/anchors"; trail = "/**"; }
++ { path = "pki/trust"; trail = "/"; }
++ { path = "pki/trust"; trail = "/*"; }
++ { path = "pki/trust/anchors"; trail = "/"; }
++ { path = "pki/trust/anchors"; trail = "/**"; }
++ ];
+ "abstractions/ssl_keys" = ''
+ # security.acme NixOS module
+ r /var/lib/acme/*/full.pem,
+ '';
+ "abstractions/vulkan" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/vulkan"
-+ ${etcRule {path="vulkan/icd.d"; trail="/";}}
-+ ${etcRule {path="vulkan/icd.d"; trail="/*.json";}}
++ ${etcRule { path = "vulkan/icd.d"; trail = "/"; }}
++ ${etcRule { path = "vulkan/icd.d"; trail = "/*.json"; }}
+ '';
+ "abstractions/winbind" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/winbind"
-+ ${etcRule {path="samba"; trail="/smb.conf";}}
-+ ${etcRule {path="samba"; trail="/dhcp.conf";}}
++ ${etcRule { path = "samba"; trail = "/smb.conf"; }}
++ ${etcRule { path = "samba"; trail = "/dhcp.conf"; }}
+ '';
+ "abstractions/X" = ''
+ include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/X"
-+ ${etcRule {path="X11/cursors"; trail="/";}}
-+ ${etcRule {path="X11/cursors"; trail="/**";}}
++ ${etcRule { path = "X11/cursors"; trail = "/"; }}
++ ${etcRule { path = "X11/cursors"; trail = "/**"; }}
+ '';
+};
+}
+ # would not work if the second one were to re-include <tunables/global>.
+'';
+}
+diff --git a/nixos/modules/security/misc.nix b/nixos/modules/security/misc.nix
+index d51dbbb77f7..e7abc1e0d59 100644
+--- a/nixos/modules/security/misc.nix
++++ b/nixos/modules/security/misc.nix
+@@ -7,6 +7,10 @@ with lib;
+ maintainers = [ maintainers.joachifm ];
+ };
+
++ imports = [
++ (lib.mkRenamedOptionModule [ "security" "virtualization" "flushL1DataCache" ] [ "security" "virtualisation" "flushL1DataCache" ])
++ ];
++
+ options = {
+ security.allowUserNamespaces = mkOption {
+ type = types.bool;
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
-index 103cf205012..8216c03795a 100644
+index 103cf205012..1c49131d789 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
-@@ -895,6 +895,61 @@ in
+@@ -895,6 +895,81 @@ in
runuser-l = { rootOK = true; unixAuth = false; };
};
+ security.apparmor.includes."abstractions/pam" = let
+ isEnabled = test: fold or false (map test (attrValues config.security.pam.services));
-+ in ''
-+ ${lib.concatMapStringsSep "\n"
-+ (name: "r ${config.environment.etc."pam.d/${name}".source},")
-+ (attrNames config.security.pam.services)}
++ in
++ lib.concatMapStringsSep "\n"
++ (name: "r ${config.environment.etc."pam.d/${name}".source},")
++ (attrNames config.security.pam.services) +
++ ''
+ mr ${getLib pkgs.pam}/lib/security/pam_filter/*,
+ mr ${getLib pkgs.pam}/lib/security/pam_*.so,
+ r ${getLib pkgs.pam}/lib/security/,
-+ ${optionalString use_ldap
-+ "mr ${pam_ldap}/lib/security/pam_ldap.so,"}
-+ ${optionalString config.services.sssd.enable
-+ "mr ${pkgs.sssd}/lib/security/pam_sss.so,"}
-+ ${optionalString config.krb5.enable ''
++ '' +
++ optionalString use_ldap ''
++ mr ${pam_ldap}/lib/security/pam_ldap.so,
++ '' +
++ optionalString config.services.sssd.enable ''
++ mr ${pkgs.sssd}/lib/security/pam_sss.so,
++ '' +
++ optionalString config.krb5.enable ''
+ mr ${pam_krb5}/lib/security/pam_krb5.so,
+ mr ${pam_ccreds}/lib/security/pam_ccreds.so,
-+ ''}
-+ ${optionalString (isEnabled (cfg: cfg.googleOsLoginAccountVerification)) ''
++ '' +
++ optionalString (isEnabled (cfg: cfg.googleOsLoginAccountVerification)) ''
+ mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so,
+ mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_admin.so,
-+ ''}
-+ ${optionalString (isEnabled (cfg: cfg.googleOsLoginAuthentication))
-+ "mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so,"}
-+ ${optionalString (config.security.pam.enableSSHAgentAuth && isEnabled (cfg: cfg.sshAgentAuth))
-+ "mr ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.fprintAuth))
-+ "mr ${pkgs.fprintd}/lib/security/pam_fprintd.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.u2fAuth))
-+ "mr ${pkgs.pam_u2f}/lib/security/pam_u2f.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.usbAuth))
-+ "mr ${pkgs.pam_usb}/lib/security/pam_usb.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.oathAuth))
-+ "mr ${pkgs.oathToolkit}/lib/security/pam_oath.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.yubicoAuth))
-+ "mr ${pkgs.yubico-pam}/lib/security/pam_yubico.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.duoSecurity.enable))
-+ "mr ${pkgs.duo-unix}/lib/security/pam_duo.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.otpwAuth))
-+ "mr ${pkgs.otpw}/lib/security/pam_otpw.so,"}
-+ ${optionalString config.security.pam.enableEcryptfs
-+ "mr ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.pamMount))
-+ "mr ${pkgs.pam_mount}/lib/security/pam_mount.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.enableGnomeKeyring))
-+ "mr ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.startSession))
-+ "mr ${pkgs.systemd}/lib/security/pam_systemd.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.enableAppArmor) && config.security.apparmor.enable)
-+ "mr ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.enableKwallet))
-+ "mr ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so,"}
-+ ${optionalString config.virtualisation.lxc.lxcfs.enable
-+ "mr ${pkgs.lxc}/lib/security/pam_cgfs.so"}
-+ '';
-+
++ '' +
++ optionalString (isEnabled (cfg: cfg.googleOsLoginAuthentication)) ''
++ mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so,
++ '' +
++ optionalString (config.security.pam.enableSSHAgentAuth
++ && isEnabled (cfg: cfg.sshAgentAuth)) ''
++ mr ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so,
++ '' +
++ optionalString (isEnabled (cfg: cfg.fprintAuth)) ''
++ mr ${pkgs.fprintd}/lib/security/pam_fprintd.so,
++ '' +
++ optionalString (isEnabled (cfg: cfg.u2fAuth)) ''
++ mr ${pkgs.pam_u2f}/lib/security/pam_u2f.so,
++ '' +
++ optionalString (isEnabled (cfg: cfg.usbAuth)) ''
++ mr ${pkgs.pam_usb}/lib/security/pam_usb.so,
++ '' +
++ optionalString (isEnabled (cfg: cfg.oathAuth)) ''
++ "mr ${pkgs.oathToolkit}/lib/security/pam_oath.so,
++ '' +
++ optionalString (isEnabled (cfg: cfg.yubicoAuth)) ''
++ mr ${pkgs.yubico-pam}/lib/security/pam_yubico.so,
++ '' +
++ optionalString (isEnabled (cfg: cfg.duoSecurity.enable)) ''
++ mr ${pkgs.duo-unix}/lib/security/pam_duo.so,
++ '' +
++ optionalString (isEnabled (cfg: cfg.otpwAuth)) ''
++ mr ${pkgs.otpw}/lib/security/pam_otpw.so,
++ '' +
++ optionalString config.security.pam.enableEcryptfs ''
++ mr ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so,
++ '' +
++ optionalString (isEnabled (cfg: cfg.pamMount)) ''
++ mr ${pkgs.pam_mount}/lib/security/pam_mount.so,
++ '' +
++ optionalString (isEnabled (cfg: cfg.enableGnomeKeyring)) ''
++ mr ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so,
++ '' +
++ optionalString (isEnabled (cfg: cfg.startSession)) ''
++ mr ${pkgs.systemd}/lib/security/pam_systemd.so,
++ '' +
++ optionalString (isEnabled (cfg: cfg.enableAppArmor)
++ && config.security.apparmor.enable) ''
++ mr ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so,
++ '' +
++ optionalString (isEnabled (cfg: cfg.enableKwallet)) ''
++ mr ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so,
++ '' +
++ optionalString config.virtualisation.lxc.lxcfs.enable ''
++ mr ${pkgs.lxc}/lib/security/pam_cgfs.so
++ '';
};
}
};
}
diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix
-index 4b2adf4cc69..335dc67673f 100644
+index 96e8d68ae50..6b6f4b6e652 100644
--- a/nixos/modules/virtualisation/lxd.nix
+++ b/nixos/modules/virtualisation/lxd.nix
-@@ -83,11 +83,15 @@ in {
+@@ -97,11 +97,17 @@ in {
+ # does a bunch of unrelated things.
+ systemd.tmpfiles.rules = [ "d /var/lib/lxc/rootfs 0755 root root -" ];
- security.apparmor = {
- enable = true;
-- profiles = [
-- "${cfg.lxcPackage}/etc/apparmor.d/usr.bin.lxc-start"
-- "${cfg.lxcPackage}/etc/apparmor.d/lxc-containers"
-- ];
- packages = [ cfg.lxcPackage ];
+- security.apparmor.packages = [ cfg.lxcPackage ];
+- security.apparmor.profiles = [
+- "${cfg.lxcPackage}/etc/apparmor.d/lxc-containers"
+- "${cfg.lxcPackage}/etc/apparmor.d/usr.bin.lxc-start"
+- ];
++ security.apparmor = {
++ packages = [ cfg.lxcPackage ];
+ policies = {
+ "bin.lxc-start".profile = ''
+ include ${cfg.lxcPackage}/etc/apparmor.d/usr.bin.lxc-start
+ include ${cfg.lxcPackage}/etc/apparmor.d/lxc-containers
+ '';
+ };
- };
++ };
# TODO: remove once LXD gets proper support for cgroupsv2
+ # (currently most of the e.g. CPU accounting stuff doesn't work)
+diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
+index fb45ec1a310..957d052ace1 100644
+--- a/nixos/tests/all-tests.nix
++++ b/nixos/tests/all-tests.nix
+@@ -25,6 +25,7 @@ in
+ acme = handleTest ./acme.nix {};
+ agda = handleTest ./agda.nix {};
+ ammonite = handleTest ./ammonite.nix {};
++ apparmor = handleTest ./apparmor.nix {};
+ atd = handleTest ./atd.nix {};
+ avahi = handleTest ./avahi.nix {};
+ avahi-with-resolved = handleTest ./avahi.nix { networkd = true; };
+diff --git a/nixos/tests/apparmor.nix b/nixos/tests/apparmor.nix
+new file mode 100644
+index 00000000000..c6daa8e67de
+--- /dev/null
++++ b/nixos/tests/apparmor.nix
+@@ -0,0 +1,82 @@
++import ./make-test-python.nix ({ pkgs, ... } : {
++ name = "apparmor";
++ meta = with pkgs.lib.maintainers; {
++ maintainers = [ julm ];
++ };
++
++ machine =
++ { lib, pkgs, config, ... }:
++ with lib;
++ {
++ security.apparmor.enable = mkDefault true;
++ };
++
++ testScript =
++ ''
++ machine.wait_for_unit("multi-user.target")
++
++ with subtest("AppArmor profiles are loaded"):
++ machine.succeed("systemctl status apparmor.service")
++
++ # AppArmor securityfs
++ with subtest("AppArmor securityfs is mounted"):
++ machine.succeed("mountpoint -q /sys/kernel/security")
++ machine.succeed("cat /sys/kernel/security/apparmor/profiles")
++
++ # Test apparmorRulesFromClosure by:
++ # 1. Prepending a string of the relevant packages' name and version on each line.
++ # 2. Sorting according to those strings.
++ # 3. Removing those prepended strings.
++ # 4. Using `diff` against the expected output.
++ with subtest("apparmorRulesFromClosure"):
++ machine.succeed(
++ "${pkgs.diffutils}/bin/diff ${pkgs.writeText "expected.rules" ''
++ mr ${pkgs.bash}/lib/**.so*,
++ r ${pkgs.bash},
++ r ${pkgs.bash}/etc/**,
++ r ${pkgs.bash}/lib/**,
++ r ${pkgs.bash}/share/**,
++ x ${pkgs.bash}/foo/**,
++ mr ${pkgs.glibc}/lib/**.so*,
++ r ${pkgs.glibc},
++ r ${pkgs.glibc}/etc/**,
++ r ${pkgs.glibc}/lib/**,
++ r ${pkgs.glibc}/share/**,
++ x ${pkgs.glibc}/foo/**,
++ mr ${pkgs.libcap}/lib/**.so*,
++ r ${pkgs.libcap},
++ r ${pkgs.libcap}/etc/**,
++ r ${pkgs.libcap}/lib/**,
++ r ${pkgs.libcap}/share/**,
++ x ${pkgs.libcap}/foo/**,
++ mr ${pkgs.libcap.lib}/lib/**.so*,
++ r ${pkgs.libcap.lib},
++ r ${pkgs.libcap.lib}/etc/**,
++ r ${pkgs.libcap.lib}/lib/**,
++ r ${pkgs.libcap.lib}/share/**,
++ x ${pkgs.libcap.lib}/foo/**,
++ mr ${pkgs.libidn2.out}/lib/**.so*,
++ r ${pkgs.libidn2.out},
++ r ${pkgs.libidn2.out}/etc/**,
++ r ${pkgs.libidn2.out}/lib/**,
++ r ${pkgs.libidn2.out}/share/**,
++ x ${pkgs.libidn2.out}/foo/**,
++ mr ${pkgs.libunistring}/lib/**.so*,
++ r ${pkgs.libunistring},
++ r ${pkgs.libunistring}/etc/**,
++ r ${pkgs.libunistring}/lib/**,
++ r ${pkgs.libunistring}/share/**,
++ x ${pkgs.libunistring}/foo/**,
++ ''} ${pkgs.runCommand "actual.rules" { preferLocalBuild = true; } ''
++ ${pkgs.gnused}/bin/sed -e 's:^[^ ]* ${builtins.storeDir}/[^,/-]*-\([^/,]*\):\1 \0:' ${
++ pkgs.apparmorRulesFromClosure {
++ name = "ping";
++ additionalRules = ["x $path/foo/**"];
++ } [ pkgs.libcap ]
++ } |
++ ${pkgs.coreutils}/bin/sort -n -k1 |
++ ${pkgs.gnused}/bin/sed -e 's:^[^ ]* ::' >$out
++ ''}"
++ )
++ '';
++})
diff --git a/pkgs/applications/networking/p2p/transmission/default.nix b/pkgs/applications/networking/p2p/transmission/default.nix
-index 8cc674b1ea7..bf09c4be1db 100644
+index 7e8b6b671cd..b2519eb2fa0 100644
--- a/pkgs/applications/networking/p2p/transmission/default.nix
+++ b/pkgs/applications/networking/p2p/transmission/default.nix
@@ -20,6 +20,7 @@
description = "A fast, easy and free BitTorrent client";
longDescription = ''
diff --git a/pkgs/os-specific/linux/apparmor/default.nix b/pkgs/os-specific/linux/apparmor/default.nix
-index 935b5e65b1f..5ec86c49660 100644
+index 935b5e65b1f..a07cd5070d2 100644
--- a/pkgs/os-specific/linux/apparmor/default.nix
+++ b/pkgs/os-specific/linux/apparmor/default.nix
@@ -10,26 +10,37 @@
+ , name ? ""
+ }: rootPaths: runCommand
+ ( "apparmor-closure-rules"
-+ + lib.optionalString (name != "") "-${name}") {} ''
++ + lib.optionalString (name != "") "-${name}" ) {} ''
+ touch $out
+ while read -r path
+ do printf >>$out "%s,\n" ${lib.concatMapStringsSep " " (x: "\"${x}\"") (baseRules ++ additionalRules)}
description = "Collection of common network programs";
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
-index bc5c1b88046..b3fee4ce167 100644
+index 8dfaf25fc04..3c055686e2e 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
-@@ -18758,7 +18758,7 @@ in
+@@ -19105,7 +19105,7 @@ in
inherit (callPackages ../os-specific/linux/apparmor { python = python3; })
libapparmor apparmor-utils apparmor-bin-utils apparmor-parser apparmor-pam