From 4d07e306ade6e3f29b0f6a8b47208312d6786d21 Mon Sep 17 00:00:00 2001 From: Grimmauld Date: Sun, 17 Nov 2024 18:03:04 +0100 Subject: [PATCH 1/5] nixos/apparmor: Format --- nixos/modules/security/apparmor.nix | 262 +++++++++++++++++----------- 1 file changed, 157 insertions(+), 105 deletions(-) diff --git a/nixos/modules/security/apparmor.nix b/nixos/modules/security/apparmor.nix index e463e48f675e9..87dde1ad50a9d 100644 --- a/nixos/modules/security/apparmor.nix +++ b/nixos/modules/security/apparmor.nix @@ -1,20 +1,42 @@ -{ config, lib, pkgs, ... }: +{ + config, + lib, + pkgs, + ... +}: let - inherit (builtins) attrNames head map match readFile; + inherit (builtins) + attrNames + head + map + match + readFile + ; inherit (lib) types; inherit (config.environment) etc; cfg = config.security.apparmor; - mkDisableOption = name: lib.mkEnableOption name // { - default = true; - example = false; - }; + mkDisableOption = + name: + lib.mkEnableOption name + // { + default = true; + example = false; + }; enabledPolicies = lib.filterAttrs (n: p: p.enable) cfg.policies; in { imports = [ - (lib.mkRemovedOptionModule [ "security" "apparmor" "confineSUIDApplications" ] "Please use the new options: `security.apparmor.policies..enable'.") - (lib.mkRemovedOptionModule [ "security" "apparmor" "profiles" ] "Please use the new option: `security.apparmor.policies'.") + (lib.mkRemovedOptionModule [ + "security" + "apparmor" + "confineSUIDApplications" + ] "Please use the new options: `security.apparmor.policies..enable'.") + (lib.mkRemovedOptionModule [ + "security" + "apparmor" + "profiles" + ] "Please use the new option: `security.apparmor.policies'.") apparmor/includes.nix apparmor/profiles.nix ]; @@ -42,22 +64,27 @@ in description = '' AppArmor policies. ''; - type = types.attrsOf (types.submodule ({ name, config, ... }: { - 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 { - description = "The policy of the profile."; - type = types.lines; - apply = pkgs.writeText name; - }; - }; - })); - default = {}; + type = types.attrsOf ( + types.submodule ( + { name, config, ... }: + { + 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 { + description = "The policy of the profile."; + type = types.lines; + apply = pkgs.writeText name; + }; + }; + } + ) + ); + default = { }; }; includes = lib.mkOption { type = types.attrsOf types.lines; - default = {}; + default = { }; description = '' List of paths to be added to AppArmor's searched paths when resolving `include` directives. @@ -66,7 +93,7 @@ in }; packages = lib.mkOption { type = types.listOf types.package; - default = []; + default = [ ]; description = "List of packages to be added to AppArmor's include path"; }; enableCache = lib.mkEnableOption '' @@ -90,13 +117,12 @@ in }; config = lib.mkIf cfg.enable { - assertions = map (policy: - { assertion = match ".*/.*" policy == null; - message = "`security.apparmor.policies.\"${policy}\"' must not contain a slash."; - # Because, for instance, aa-remove-unknown uses profiles_names_list() in rc.apparmor.functions - # which does not recurse into sub-directories. - } - ) (attrNames cfg.policies); + assertions = map (policy: { + assertion = match ".*/.*" policy == null; + message = "`security.apparmor.policies.\"${policy}\"' must not contain a slash."; + # Because, for instance, aa-remove-unknown uses profiles_names_list() in rc.apparmor.functions + # which does not recurse into sub-directories. + }) (attrNames cfg.policies); environment.systemPackages = [ pkgs.apparmor-utils @@ -105,67 +131,81 @@ in 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 + lib.mapAttrsToList (name: p: { + inherit name; + path = p.profile; + }) enabledPolicies + ++ lib.mapAttrsToList (name: path: { inherit name path; }) cfg.includes ); - environment.etc."apparmor/parser.conf".text = '' + 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; + '' + + lib.concatMapStrings (p: "Include ${p}/etc/apparmor.d\n") cfg.packages; # For aa-logprof - environment.etc."apparmor/apparmor.conf".text = '' - ''; + environment.etc."apparmor/apparmor.conf".text = ''''; # For aa-logprof environment.etc."apparmor/severity.db".source = pkgs.apparmor-utils + "/etc/apparmor/severity.db"; - environment.etc."apparmor/logprof.conf".source = pkgs.runCommand "logprof.conf" { - header = '' - [settings] - # /etc/apparmor.d/ is read-only on NixOS - profiledir = /var/cache/apparmor/logprof - inactive_profiledir = /etc/apparmor.d/disable - # Use: journalctl -b --since today --grep audit: | aa-logprof - logfiles = /dev/stdin - - parser = ${pkgs.apparmor-parser}/bin/apparmor_parser - ldd = ${pkgs.glibc.bin}/bin/ldd - logger = ${pkgs.util-linux}/bin/logger - - # customize how file ownership permissions are presented - # 0 - off - # 1 - default of what ever mode the log reported - # 2 - force the new permissions to be user - # 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} - - [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" ]; - } '' - cp $headerPath $out - sed '1,/\[qualifiers\]/d' $footer >> $out - ''; - - boot.kernelParams = [ "apparmor=1" "security=apparmor" ]; + environment.etc."apparmor/logprof.conf".source = + pkgs.runCommand "logprof.conf" + { + header = '' + [settings] + # /etc/apparmor.d/ is read-only on NixOS + profiledir = /var/cache/apparmor/logprof + inactive_profiledir = /etc/apparmor.d/disable + # Use: journalctl -b --since today --grep audit: | aa-logprof + logfiles = /dev/stdin + + parser = ${pkgs.apparmor-parser}/bin/apparmor_parser + ldd = ${pkgs.glibc.bin}/bin/ldd + logger = ${pkgs.util-linux}/bin/logger + + # customize how file ownership permissions are presented + # 0 - off + # 1 - default of what ever mode the log reported + # 2 - force the new permissions to be user + # 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 + } + + [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" ]; + } + '' + cp $headerPath $out + sed '1,/\[qualifiers\]/d' $footer >> $out + ''; + + boot.kernelParams = [ + "apparmor=1" + "security=apparmor" + ]; systemd.services.apparmor = { after = [ "local-fs.target" "systemd-journald-audit.socket" ]; - before = [ "sysinit.target" "shutdown.target" ]; + before = [ + "sysinit.target" + "shutdown.target" + ]; conflicts = [ "shutdown.target" ]; wantedBy = [ "multi-user.target" ]; unitConfig = { - Description="Load AppArmor policies"; + Description = "Load AppArmor policies"; DefaultDependencies = "no"; ConditionSecurity = "apparmor"; }; @@ -176,37 +216,49 @@ in etc."apparmor/parser.conf".source etc."apparmor.d".source ]; - serviceConfig = let - killUnconfinedConfinables = pkgs.writeShellScript "apparmor-kill" '' - set -eu - ${pkgs.apparmor-bin-utils}/bin/aa-status --json | - ${pkgs.jq}/bin/jq --raw-output '.processes | .[] | .[] | select (.status == "unconfined") | .pid' | - xargs --verbose --no-run-if-empty --delimiter='\n' \ - kill - ''; - commonOpts = p: "--verbose --show-cache ${lib.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; - 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 ++ - # 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). - # Note that this does not remove profiles dynamically generated by libvirt. - [ "${pkgs.apparmor-utils}/bin/aa-remove-unknown" ] ++ - # Optionally 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; - ExecStop = "${pkgs.apparmor-utils}/bin/aa-teardown"; - CacheDirectory = [ "apparmor" "apparmor/logprof" ]; - CacheDirectoryMode = "0700"; - }; + serviceConfig = + let + killUnconfinedConfinables = pkgs.writeShellScript "apparmor-kill" '' + set -eu + ${pkgs.apparmor-bin-utils}/bin/aa-status --json | + ${pkgs.jq}/bin/jq --raw-output '.processes | .[] | .[] | select (.status == "unconfined") | .pid' | + xargs --verbose --no-run-if-empty --delimiter='\n' \ + kill + ''; + commonOpts = + p: "--verbose --show-cache ${lib.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; + 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 + ++ + # 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). + # Note that this does not remove profiles dynamically generated by libvirt. + [ "${pkgs.apparmor-utils}/bin/aa-remove-unknown" ] + ++ + # Optionally 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; + ExecStop = "${pkgs.apparmor-utils}/bin/aa-teardown"; + CacheDirectory = [ + "apparmor" + "apparmor/logprof" + ]; + CacheDirectoryMode = "0700"; + }; }; }; From db05ce5ef75ef1c5496ccfafbb80cd8fa2c3e818 Mon Sep 17 00:00:00 2001 From: Grimmauld Date: Tue, 3 Dec 2024 20:14:36 +0100 Subject: [PATCH 2/5] nixos/tests/apparmor.nix: Format --- nixos/tests/apparmor.nix | 130 +++++++++++++++++++++------------------ 1 file changed, 71 insertions(+), 59 deletions(-) diff --git a/nixos/tests/apparmor.nix b/nixos/tests/apparmor.nix index f75c0c72718d1..e0b29b3be819d 100644 --- a/nixos/tests/apparmor.nix +++ b/nixos/tests/apparmor.nix @@ -1,15 +1,24 @@ -import ./make-test-python.nix ({ pkgs, lib, ... } : { - name = "apparmor"; - meta.maintainers = with lib.maintainers; [ julm grimmauld ]; +import ./make-test-python.nix ( + { pkgs, lib, ... }: + { + name = "apparmor"; + meta.maintainers = with lib.maintainers; [ + julm + grimmauld + ]; - nodes.machine = - { lib, pkgs, config, ... }: - { - security.apparmor.enable = lib.mkDefault true; - }; + nodes.machine = + { + lib, + pkgs, + config, + ... + }: + { + security.apparmor.enable = lib.mkDefault true; + }; - testScript = - '' + testScript = '' machine.wait_for_unit("multi-user.target") with subtest("AppArmor profiles are loaded"): @@ -28,58 +37,61 @@ import ./make-test-python.nix ({ pkgs, lib, ... } : { with subtest("apparmorRulesFromClosure"): machine.succeed( "${pkgs.diffutils}/bin/diff -u ${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/**, - mr ${pkgs.glibc.libgcc}/lib/**.so*, - r ${pkgs.glibc.libgcc}, - r ${pkgs.glibc.libgcc}/etc/**, - r ${pkgs.glibc.libgcc}/lib/**, - r ${pkgs.glibc.libgcc}/share/**, - x ${pkgs.glibc.libgcc}/foo/**, - ''} ${pkgs.runCommand "actual.rules" { preferLocalBuild = true; } '' + 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/**, + mr ${pkgs.glibc.libgcc}/lib/**.so*, + r ${pkgs.glibc.libgcc}, + r ${pkgs.glibc.libgcc}/etc/**, + r ${pkgs.glibc.libgcc}/lib/**, + r ${pkgs.glibc.libgcc}/share/**, + x ${pkgs.glibc.libgcc}/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.apparmorRulesFromClosure { + name = "ping"; + additionalRules = [ "x $path/foo/**" ]; + } [ pkgs.libcap ] } | ${pkgs.coreutils}/bin/sort -n -k1 | ${pkgs.gnused}/bin/sed -e 's:^[^ ]* ::' >$out - ''}" + '' + }" ) ''; -}) + } +) From 1f750cfa3de8eb47e6d6281cd1c940a7be41540b Mon Sep 17 00:00:00 2001 From: Grimmauld Date: Tue, 3 Dec 2024 20:17:47 +0100 Subject: [PATCH 3/5] nixos/tests/apparmor.nix: fix expected.rules --- nixos/tests/apparmor.nix | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/nixos/tests/apparmor.nix b/nixos/tests/apparmor.nix index e0b29b3be819d..fad0e31bfffd1 100644 --- a/nixos/tests/apparmor.nix +++ b/nixos/tests/apparmor.nix @@ -37,47 +37,68 @@ import ./make-test-python.nix ( with subtest("apparmorRulesFromClosure"): machine.succeed( "${pkgs.diffutils}/bin/diff -u ${pkgs.writeText "expected.rules" '' + ixr ${pkgs.bash}/libexec/**, mr ${pkgs.bash}/lib/**.so*, + mr ${pkgs.bash}/lib64/**.so*, + mr ${pkgs.bash}/share/**, r ${pkgs.bash}, r ${pkgs.bash}/etc/**, r ${pkgs.bash}/lib/**, - r ${pkgs.bash}/share/**, + r ${pkgs.bash}/lib64/**, x ${pkgs.bash}/foo/**, + ixr ${pkgs.glibc}/libexec/**, mr ${pkgs.glibc}/lib/**.so*, + mr ${pkgs.glibc}/lib64/**.so*, + mr ${pkgs.glibc}/share/**, r ${pkgs.glibc}, r ${pkgs.glibc}/etc/**, r ${pkgs.glibc}/lib/**, - r ${pkgs.glibc}/share/**, + r ${pkgs.glibc}/lib64/**, x ${pkgs.glibc}/foo/**, + ixr ${pkgs.libcap}/libexec/**, mr ${pkgs.libcap}/lib/**.so*, + mr ${pkgs.libcap}/lib64/**.so*, + mr ${pkgs.libcap}/share/**, r ${pkgs.libcap}, r ${pkgs.libcap}/etc/**, r ${pkgs.libcap}/lib/**, - r ${pkgs.libcap}/share/**, + r ${pkgs.libcap}/lib64/**, x ${pkgs.libcap}/foo/**, + ixr ${pkgs.libcap.lib}/libexec/**, mr ${pkgs.libcap.lib}/lib/**.so*, + mr ${pkgs.libcap.lib}/lib64/**.so*, + mr ${pkgs.libcap.lib}/share/**, r ${pkgs.libcap.lib}, r ${pkgs.libcap.lib}/etc/**, r ${pkgs.libcap.lib}/lib/**, - r ${pkgs.libcap.lib}/share/**, + r ${pkgs.libcap.lib}/lib64/**, x ${pkgs.libcap.lib}/foo/**, + ixr ${pkgs.libidn2.out}/libexec/**, mr ${pkgs.libidn2.out}/lib/**.so*, + mr ${pkgs.libidn2.out}/lib64/**.so*, + mr ${pkgs.libidn2.out}/share/**, r ${pkgs.libidn2.out}, r ${pkgs.libidn2.out}/etc/**, r ${pkgs.libidn2.out}/lib/**, - r ${pkgs.libidn2.out}/share/**, + r ${pkgs.libidn2.out}/lib64/**, x ${pkgs.libidn2.out}/foo/**, + ixr ${pkgs.libunistring}/libexec/**, mr ${pkgs.libunistring}/lib/**.so*, + mr ${pkgs.libunistring}/lib64/**.so*, + mr ${pkgs.libunistring}/share/**, r ${pkgs.libunistring}, r ${pkgs.libunistring}/etc/**, r ${pkgs.libunistring}/lib/**, - r ${pkgs.libunistring}/share/**, + r ${pkgs.libunistring}/lib64/**, x ${pkgs.libunistring}/foo/**, + ixr ${pkgs.glibc.libgcc}/libexec/**, mr ${pkgs.glibc.libgcc}/lib/**.so*, + mr ${pkgs.glibc.libgcc}/lib64/**.so*, + mr ${pkgs.glibc.libgcc}/share/**, r ${pkgs.glibc.libgcc}, r ${pkgs.glibc.libgcc}/etc/**, r ${pkgs.glibc.libgcc}/lib/**, - r ${pkgs.glibc.libgcc}/share/**, + r ${pkgs.glibc.libgcc}/lib64/**, x ${pkgs.glibc.libgcc}/foo/**, ''} ${ pkgs.runCommand "actual.rules" { preferLocalBuild = true; } '' From e87b9b1f3e4c5d4587fd05f5b1213be74d904a42 Mon Sep 17 00:00:00 2001 From: Grimmauld Date: Sun, 17 Nov 2024 18:27:49 +0100 Subject: [PATCH 4/5] nixos/apparmor: profile activation tristate and profile path support --- .../manual/release-notes/rl-2505.section.md | 3 + nixos/modules/security/apparmor.nix | 98 +++++++++++-------- 2 files changed, 61 insertions(+), 40 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2505.section.md b/nixos/doc/manual/release-notes/rl-2505.section.md index 595b6af0e339d..7d01ab2a4c5c2 100644 --- a/nixos/doc/manual/release-notes/rl-2505.section.md +++ b/nixos/doc/manual/release-notes/rl-2505.section.md @@ -162,6 +162,9 @@ | virtualBoxOVA | virtualbox-vagrant.box | nixos-image-vagrant-virtualbox-25.05pre-git-x86_64-linux.ova | | vmwareImage | nixos-25.05pre-git-x86_64-linux.vmdk | nixos-image-vmware-25.05pre-git-x86_64-linux.vmdk | +- `security.apparmor.policies..enforce` and `security.apparmor.policies..enable` were removed. + Configuring the state of apparmor policies must now be done using `security.apparmor.policies..state` tristate option. + - the notmuch vim plugin now lives in a separate output of the `notmuch` package. Installing `notmuch` will not bring the notmuch vim package anymore, add `vimPlugins.notmuch-vim` to your (Neo)vim configuration if you want the diff --git a/nixos/modules/security/apparmor.nix b/nixos/modules/security/apparmor.nix index 87dde1ad50a9d..a4c2f9e29fc34 100644 --- a/nixos/modules/security/apparmor.nix +++ b/nixos/modules/security/apparmor.nix @@ -5,24 +5,16 @@ ... }: let - inherit (builtins) - attrNames - head - map - match - readFile - ; inherit (lib) types; inherit (config.environment) etc; cfg = config.security.apparmor; - mkDisableOption = - name: - lib.mkEnableOption name - // { - default = true; - example = false; - }; - enabledPolicies = lib.filterAttrs (n: p: p.enable) cfg.policies; + enabledPolicies = lib.filterAttrs (n: p: p.state != "disable") cfg.policies; + buildPolicyPath = n: p: lib.defaultTo (pkgs.writeText n p.profile) p.path; + + # Accessing submodule options when not defined results in an error thunk rather than a regular option object + # We can emulate the behavior of `