···
inherit (config.environment) etc;
cfg = config.security.apparmor;
20
-
lib.mkEnableOption name
25
-
enabledPolicies = lib.filterAttrs (n: p: p.enable) cfg.policies;
11
+
enabledPolicies = lib.filterAttrs (n: p: p.state != "disable") cfg.policies;
12
+
buildPolicyPath = n: p: lib.defaultTo (pkgs.writeText n p.profile) p.path;
14
+
# Accessing submodule options when not defined results in an error thunk rather than a regular option object
15
+
# We can emulate the behavior of `<option>.isDefined` by attempting to evaluate it instead
16
+
# This is required because getting isDefined on a submodule is not possible in global module asserts.
17
+
submoduleOptionIsDefined = value: (builtins.tryEval value).success;
···
"confineSUIDApplications"
34
-
] "Please use the new options: `security.apparmor.policies.<policy>.enable'.")
26
+
] "Please use the new options: `security.apparmor.policies.<policy>.state'.")
(lib.mkRemovedOptionModule [
···
69
-
{ name, config, ... }:
72
-
enable = mkDisableOption "loading of the profile into the kernel";
73
-
enforce = mkDisableOption "enforcing of the policy or only complain in the logs";
74
-
profile = lib.mkOption {
75
-
description = "The policy of the profile.";
77
-
apply = pkgs.writeText name;
62
+
state = lib.mkOption {
63
+
description = "How strictly this policy should be enforced";
69
+
# should enforce really be the default?
70
+
# the docs state that this should only be used once one is REALLY sure nothing's gonna break
71
+
default = "enforce";
74
+
profile = lib.mkOption {
75
+
description = "The profile file contents. Incompatible with path.";
79
+
path = lib.mkOption {
80
+
description = "A path of a profile file to include. Incompatible with profile.";
81
+
type = types.nullOr types.path;
···
config = lib.mkIf cfg.enable {
120
-
assertions = map (policy: {
121
-
assertion = match ".*/.*" policy == null;
122
-
message = "`security.apparmor.policies.\"${policy}\"' must not contain a slash.";
123
-
# Because, for instance, aa-remove-unknown uses profiles_names_list() in rc.apparmor.functions
124
-
# which does not recurse into sub-directories.
125
-
}) (attrNames cfg.policies);
124
+
assertions = lib.concatLists (
125
+
lib.mapAttrsToList (policyName: policyCfg: [
127
+
assertion = builtins.match ".*/.*" policyName == null;
128
+
message = "`security.apparmor.policies.\"${policyName}\"' must not contain a slash.";
129
+
# Because, for instance, aa-remove-unknown uses profiles_names_list() in rc.apparmor.functions
130
+
# which does not recurse into sub-directories.
133
+
assertion = lib.xor (policyCfg.path != null) (submoduleOptionIsDefined policyCfg.profile);
134
+
message = "`security.apparmor.policies.\"${policyName}\"` must define exactly one of either path or profile.";
environment.systemPackages = [
···
# because aa-remove-unknown reads profiles from all /etc/apparmor.d/*
lib.mapAttrsToList (name: p: {
148
+
path = buildPolicyPath name p;
++ lib.mapAttrsToList (name: path: { inherit name path; }) cfg.includes
···
229
-
p: "--verbose --show-cache ${lib.optionalString (!p.enforce) "--complain "}${p.profile}";
242
+
"--verbose --show-cache ${
243
+
lib.optionalString (p.state == "complain") "--complain "
244
+
}${buildPolicyPath n p}";
ExecStartPre = "${pkgs.apparmor-utils}/bin/aa-teardown";
ExecStart = lib.mapAttrsToList (
236
-
n: p: "${pkgs.apparmor-parser}/bin/apparmor_parser --add ${commonOpts p}"
251
+
n: p: "${pkgs.apparmor-parser}/bin/apparmor_parser --add ${commonOpts n p}"
ExecStartPost = lib.optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
# Add or replace into the kernel profiles in enabledPolicies
# (because AppArmor can do that without stopping the processes already confined).
243
-
n: p: "${pkgs.apparmor-parser}/bin/apparmor_parser --replace ${commonOpts p}"
258
+
n: p: "${pkgs.apparmor-parser}/bin/apparmor_parser --replace ${commonOpts n p}"
# Remove from the kernel any profile whose name is not
···
265
-
meta.maintainers = with lib.maintainers; [ julm grimmauld ];
280
+
meta.maintainers = with lib.maintainers; [