]> Git — Sourcephile - sourcephile-nix.git/blob - nixpkgs/patches/apparmor.diff
losurdo: nix-serve: enable over wireguard
[sourcephile-nix.git] / nixpkgs / patches / apparmor.diff
1 diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix
2 index 6270ac778ae..57f3dda64cd 100644
3 --- a/maintainers/maintainer-list.nix
4 +++ b/maintainers/maintainer-list.nix
5 @@ -4779,7 +4779,7 @@
6 name = "Julien Dehos";
7 };
8 julm = {
9 - email = "julm+nix@sourcephile.fr";
10 + email = "julm+nixpkgs@sourcephile.fr";
11 github = "ju1m";
12 githubId = 21160136;
13 name = "Julien Moutinho";
14 diff --git a/nixos/doc/manual/release-notes/rl-2105.xml b/nixos/doc/manual/release-notes/rl-2105.xml
15 index b7947293c01..8abee64734d 100644
16 --- a/nixos/doc/manual/release-notes/rl-2105.xml
17 +++ b/nixos/doc/manual/release-notes/rl-2105.xml
18 @@ -795,6 +795,23 @@ environment.systemPackages = [
19 The option's description was incorrect regarding ownership management and has been simplified greatly.
20 </para>
21 </listitem>
22 + <listitem>
23 + <para>
24 + The <literal>security.apparmor</literal> module,
25 + for the <link xlink:href="https://gitlab.com/apparmor/apparmor/-/wikis/Documentation">AppArmor</link>
26 + Mandatory Access Control system,
27 + has been substantialy improved along with related tools,
28 + so that module maintainers can now more easily write AppArmor profiles for NixOS.
29 + The most notable change on the user-side is the new option <xref linkend="opt-security.apparmor.policies"/>,
30 + replacing the previous <literal>profiles</literal> option
31 + to provide a way to disable a profile
32 + and to select whether to confine in enforce mode (default)
33 + or in complain mode (see <literal>journalctl -b --grep apparmor</literal>).
34 + Security-minded users may also want to enable <xref linkend="opt-security.apparmor.killUnconfinedConfinables"/>,
35 + at the cost of having some of their processes killed
36 + when updating to a NixOS version introducing new AppArmor profiles.
37 + </para>
38 + </listitem>
39 <listitem>
40 <para>
41 The GNOME desktop manager once again installs <package>gnome3.epiphany</package> by default.
42 diff --git a/nixos/modules/config/fonts/fontconfig.nix b/nixos/modules/config/fonts/fontconfig.nix
43 index 6e7b8c4b88a..72827c5abaa 100644
44 --- a/nixos/modules/config/fonts/fontconfig.nix
45 +++ b/nixos/modules/config/fonts/fontconfig.nix
46 @@ -448,6 +448,40 @@ in
47 (mkIf cfg.enable {
48 environment.systemPackages = [ pkgs.fontconfig ];
49 environment.etc.fonts.source = "${fontconfigEtc}/etc/fonts/";
50 + security.apparmor.includes."abstractions/fonts" = ''
51 + # fonts.conf
52 + r ${pkg.out}/etc/fonts/fonts.conf,
53 +
54 + # fontconfig default config files
55 + r ${pkg.out}/etc/fonts/conf.d/*.conf,
56 +
57 + # 00-nixos-cache.conf
58 + r ${cacheConf},
59 +
60 + # 10-nixos-rendering.conf
61 + r ${renderConf},
62 +
63 + # 50-user.conf
64 + ${optionalString cfg.includeUserConf ''
65 + r ${pkg.out}/etc/fonts/conf.d.bak/50-user.conf,
66 + ''}
67 +
68 + # local.conf (indirect priority 51)
69 + ${optionalString (cfg.localConf != "") ''
70 + r ${localConf},
71 + ''}
72 +
73 + # 52-nixos-default-fonts.conf
74 + r ${defaultFontsConf},
75 +
76 + # 53-no-bitmaps.conf
77 + r ${rejectBitmaps},
78 +
79 + ${optionalString (!cfg.allowType1) ''
80 + # 53-nixos-reject-type1.conf
81 + r ${rejectType1},
82 + ''}
83 + '';
84 })
85 (mkIf cfg.enable {
86 fonts.fontconfig.confPackages = [ confPkg ];
87 diff --git a/nixos/modules/config/malloc.nix b/nixos/modules/config/malloc.nix
88 index a3eb55d8a42..fc35993b5a8 100644
89 --- a/nixos/modules/config/malloc.nix
90 +++ b/nixos/modules/config/malloc.nix
91 @@ -87,5 +87,12 @@ in
92 environment.etc."ld-nix.so.preload".text = ''
93 ${providerLibPath}
94 '';
95 + security.apparmor.includes = {
96 + "abstractions/base" = ''
97 + r /etc/ld-nix.so.preload,
98 + r ${config.environment.etc."ld-nix.so.preload".source},
99 + mr ${providerLibPath},
100 + '';
101 + };
102 };
103 }
104 diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
105 index 4a63a09ab84..43f052d150e 100644
106 --- a/nixos/modules/module-list.nix
107 +++ b/nixos/modules/module-list.nix
108 @@ -201,7 +201,6 @@
109 ./rename.nix
110 ./security/acme.nix
111 ./security/apparmor.nix
112 - ./security/apparmor-suid.nix
113 ./security/audit.nix
114 ./security/auditd.nix
115 ./security/ca.nix
116 diff --git a/nixos/modules/profiles/hardened.nix b/nixos/modules/profiles/hardened.nix
117 index 00aafc6831b..3f8f78f012a 100644
118 --- a/nixos/modules/profiles/hardened.nix
119 +++ b/nixos/modules/profiles/hardened.nix
120 @@ -36,6 +36,7 @@ with lib;
121 security.virtualisation.flushL1DataCache = mkDefault "always";
122
123 security.apparmor.enable = mkDefault true;
124 + security.apparmor.killUnconfinedConfinables = mkDefault true;
125
126 boot.kernelParams = [
127 # Slab/slub sanity checks, redzoning, and poisoning
128 diff --git a/nixos/modules/security/apparmor-suid.nix b/nixos/modules/security/apparmor-suid.nix
129 deleted file mode 100644
130 index 6c479e070e2..00000000000
131 --- a/nixos/modules/security/apparmor-suid.nix
132 +++ /dev/null
133 @@ -1,49 +0,0 @@
134 -{ config, lib, pkgs, ... }:
135 -let
136 - cfg = config.security.apparmor;
137 -in
138 -with lib;
139 -{
140 - imports = [
141 - (mkRenamedOptionModule [ "security" "virtualization" "flushL1DataCache" ] [ "security" "virtualisation" "flushL1DataCache" ])
142 - ];
143 -
144 - options.security.apparmor.confineSUIDApplications = mkOption {
145 - type = types.bool;
146 - default = true;
147 - description = ''
148 - Install AppArmor profiles for commonly-used SUID application
149 - to mitigate potential privilege escalation attacks due to bugs
150 - in such applications.
151 -
152 - Currently available profiles: ping
153 - '';
154 - };
155 -
156 - config = mkIf (cfg.confineSUIDApplications) {
157 - security.apparmor.profiles = [ (pkgs.writeText "ping" ''
158 - #include <tunables/global>
159 - /run/wrappers/bin/ping {
160 - #include <abstractions/base>
161 - #include <abstractions/consoles>
162 - #include <abstractions/nameservice>
163 -
164 - capability net_raw,
165 - capability setuid,
166 - network inet raw,
167 -
168 - ${pkgs.stdenv.cc.libc.out}/lib/*.so mr,
169 - ${pkgs.libcap.lib}/lib/libcap.so* mr,
170 - ${pkgs.attr.out}/lib/libattr.so* mr,
171 -
172 - ${pkgs.iputils}/bin/ping mixr,
173 -
174 - #/etc/modules.conf r,
175 -
176 - ## Site-specific additions and overrides. See local/README for details.
177 - ##include <local/bin.ping>
178 - }
179 - '') ];
180 - };
181 -
182 -}
183 diff --git a/nixos/modules/security/apparmor.nix b/nixos/modules/security/apparmor.nix
184 index cfc65b347bc..8683d2b487c 100644
185 --- a/nixos/modules/security/apparmor.nix
186 +++ b/nixos/modules/security/apparmor.nix
187 @@ -1,59 +1,214 @@
188 { config, lib, pkgs, ... }:
189
190 +with lib;
191 +
192 let
193 - inherit (lib) mkIf mkOption types concatMapStrings;
194 + inherit (builtins) attrNames head map match readFile;
195 + inherit (lib) types;
196 + inherit (config.environment) etc;
197 cfg = config.security.apparmor;
198 + mkDisableOption = name: mkEnableOption name // {
199 + default = true;
200 + example = false;
201 + };
202 + enabledPolicies = filterAttrs (n: p: p.enable) cfg.policies;
203 in
204
205 {
206 - options = {
207 - security.apparmor = {
208 - enable = mkOption {
209 - type = types.bool;
210 - default = false;
211 - description = "Enable the AppArmor Mandatory Access Control system.";
212 - };
213 - profiles = mkOption {
214 - type = types.listOf types.path;
215 - default = [];
216 - description = "List of files containing AppArmor profiles.";
217 - };
218 - packages = mkOption {
219 - type = types.listOf types.package;
220 - default = [];
221 - description = "List of packages to be added to apparmor's include path";
222 - };
223 - };
224 - };
225 + imports = [
226 + (mkRemovedOptionModule [ "security" "apparmor" "confineSUIDApplications" ] "Please use the new options: `security.apparmor.policies.<policy>.enable'.")
227 + (mkRemovedOptionModule [ "security" "apparmor" "profiles" ] "Please use the new option: `security.apparmor.policies'.")
228 + apparmor/includes.nix
229 + apparmor/profiles.nix
230 + ];
231
232 - config = mkIf cfg.enable {
233 - environment.systemPackages = [ pkgs.apparmor-utils ];
234 + options = {
235 + security.apparmor = {
236 + enable = mkEnableOption ''
237 + the AppArmor Mandatory Access Control system.
238
239 - boot.kernelParams = [ "apparmor=1" "security=apparmor" ];
240 + If you're enabling this module on a running system,
241 + note that a reboot will be required to activate AppArmor in the kernel.
242
243 - systemd.services.apparmor = let
244 - paths = concatMapStrings (s: " -I ${s}/etc/apparmor.d")
245 - ([ pkgs.apparmor-profiles ] ++ cfg.packages);
246 - in {
247 - after = [ "local-fs.target" ];
248 - before = [ "sysinit.target" ];
249 - wantedBy = [ "multi-user.target" ];
250 - unitConfig = {
251 - DefaultDependencies = "no";
252 - };
253 - serviceConfig = {
254 - Type = "oneshot";
255 - RemainAfterExit = "yes";
256 - ExecStart = map (p:
257 - ''${pkgs.apparmor-parser}/bin/apparmor_parser -rKv ${paths} "${p}"''
258 - ) cfg.profiles;
259 - ExecStop = map (p:
260 - ''${pkgs.apparmor-parser}/bin/apparmor_parser -Rv "${p}"''
261 - ) cfg.profiles;
262 - ExecReload = map (p:
263 - ''${pkgs.apparmor-parser}/bin/apparmor_parser --reload ${paths} "${p}"''
264 - ) cfg.profiles;
265 - };
266 - };
267 - };
268 + Also, beware that enabling this module privileges stability over security
269 + by not trying to kill unconfined but newly confinable running processes by default,
270 + which can happen because AppArmor can only confine new
271 + or already confined processes of an executable.
272 + This will happen when upgrading to a NixOS revision
273 + introducing an AppArmor profile for the executable of a running process.
274 +
275 + Enable <xref linkend="opt-security.apparmor.killUnconfinedConfinables"/>
276 + if you want this service to send a </literal>SIGTERM</literal> to those running processes'';
277 + policies = mkOption {
278 + description = ''
279 + AppArmor policies.
280 + '';
281 + type = types.attrsOf (types.submodule ({ name, config, ... }: {
282 + options = {
283 + enable = mkDisableOption "loading of the profile into the kernel";
284 + enforce = mkDisableOption "enforcing of the policy or only complain in the logs";
285 + profile = mkOption {
286 + description = "The policy of the profile.";
287 + type = types.lines;
288 + apply = pkgs.writeText name;
289 + };
290 + };
291 + }));
292 + default = {};
293 + };
294 + includes = mkOption {
295 + type = types.attrsOf types.lines;
296 + default = {};
297 + description = ''
298 + List of paths to be added to AppArmor's searched paths
299 + when resolving <literal>include</literal> directives.
300 + '';
301 + apply = mapAttrs pkgs.writeText;
302 + };
303 + packages = mkOption {
304 + type = types.listOf types.package;
305 + default = [];
306 + description = "List of packages to be added to AppArmor's include path";
307 + };
308 + enableCache = mkEnableOption ''
309 + caching of AppArmor policies
310 + in <literal>/var/cache/apparmor/</literal>.
311 +
312 + Beware that AppArmor policies almost always contain Nix store paths,
313 + and thus produce at each change of these paths
314 + a new cached version accumulating in the cache'';
315 + killUnconfinedConfinables = mkEnableOption ''
316 + killing of processes which have an AppArmor profile enabled
317 + (in <xref linkend="opt-security.apparmor.policies"/>)
318 + but are not confined (because AppArmor can only confine new processes).
319 +
320 + This is only sending a gracious <literal>SIGTERM</literal> signal to the processes,
321 + not a <literal>SIGKILL</literal>.
322 +
323 + Beware that due to a current limitation of AppArmor,
324 + only profiles with exact paths (and no name) can enable such kills'';
325 + };
326 + };
327 +
328 + config = mkIf cfg.enable {
329 + assertions = map (policy:
330 + { assertion = match ".*/.*" policy == null;
331 + message = "`security.apparmor.policies.\"${policy}\"' must not contain a slash.";
332 + # Because, for instance, aa-remove-unknown uses profiles_names_list() in rc.apparmor.functions
333 + # which does not recurse into sub-directories.
334 + }
335 + ) (attrNames cfg.policies);
336 +
337 + environment.systemPackages = [
338 + pkgs.apparmor-utils
339 + pkgs.apparmor-bin-utils
340 + ];
341 + environment.etc."apparmor.d".source = pkgs.linkFarm "apparmor.d" (
342 + # It's important to put only enabledPolicies here and not all cfg.policies
343 + # because aa-remove-unknown reads profiles from all /etc/apparmor.d/*
344 + mapAttrsToList (name: p: { inherit name; path = p.profile; }) enabledPolicies ++
345 + mapAttrsToList (name: path: { inherit name path; }) cfg.includes
346 + );
347 + environment.etc."apparmor/parser.conf".text = ''
348 + ${if cfg.enableCache then "write-cache" else "skip-cache"}
349 + cache-loc /var/cache/apparmor
350 + Include /etc/apparmor.d
351 + '' +
352 + concatMapStrings (p: "Include ${p}/etc/apparmor.d\n") cfg.packages;
353 + # For aa-logprof
354 + environment.etc."apparmor/apparmor.conf".text = ''
355 + '';
356 + # For aa-logprof
357 + environment.etc."apparmor/severity.db".source = pkgs.apparmor-utils + "/etc/apparmor/severity.db";
358 + environment.etc."apparmor/logprof.conf".source = pkgs.runCommand "logprof.conf" {
359 + header = ''
360 + [settings]
361 + # /etc/apparmor.d/ is read-only on NixOS
362 + profiledir = /var/cache/apparmor/logprof
363 + inactive_profiledir = /etc/apparmor.d/disable
364 + # Use: journalctl -b --since today --grep audit: | aa-logprof
365 + logfiles = /dev/stdin
366 +
367 + parser = ${pkgs.apparmor-parser}/bin/apparmor_parser
368 + ldd = ${pkgs.glibc.bin}/bin/ldd
369 + logger = ${pkgs.utillinux}/bin/logger
370 +
371 + # customize how file ownership permissions are presented
372 + # 0 - off
373 + # 1 - default of what ever mode the log reported
374 + # 2 - force the new permissions to be user
375 + # 3 - force all perms on the rule to be user
376 + default_owner_prompt = 1
377 +
378 + custom_includes = /etc/apparmor.d ${concatMapStringsSep " " (p: "${p}/etc/apparmor.d") cfg.packages}
379 +
380 + [qualifiers]
381 + ${pkgs.runtimeShell} = icnu
382 + ${pkgs.bashInteractive}/bin/sh = icnu
383 + ${pkgs.bashInteractive}/bin/bash = icnu
384 + ${config.users.defaultUserShell} = icnu
385 + '';
386 + footer = "${pkgs.apparmor-utils}/etc/apparmor/logprof.conf";
387 + passAsFile = [ "header" ];
388 + } ''
389 + cp $headerPath $out
390 + sed '1,/\[qualifiers\]/d' $footer >> $out
391 + '';
392 +
393 + boot.kernelParams = [ "apparmor=1" "security=apparmor" ];
394 +
395 + systemd.services.apparmor = {
396 + after = [
397 + "local-fs.target"
398 + "systemd-journald-audit.socket"
399 + ];
400 + before = [ "sysinit.target" ];
401 + wantedBy = [ "multi-user.target" ];
402 + unitConfig = {
403 + Description="Load AppArmor policies";
404 + DefaultDependencies = "no";
405 + ConditionSecurity = "apparmor";
406 + };
407 + # Reloading instead of restarting enables to load new AppArmor profiles
408 + # without necessarily restarting all services which have Requires=apparmor.service
409 + reloadIfChanged = true;
410 + restartTriggers = [
411 + etc."apparmor/parser.conf".source
412 + etc."apparmor.d".source
413 + ];
414 + serviceConfig = let
415 + killUnconfinedConfinables = pkgs.writeShellScript "apparmor-kill" ''
416 + set -eu
417 + ${pkgs.apparmor-bin-utils}/bin/aa-status --json |
418 + ${pkgs.jq}/bin/jq --raw-output '.processes | .[] | .[] | select (.status == "unconfined") | .pid' |
419 + xargs --verbose --no-run-if-empty --delimiter='\n' \
420 + kill
421 + '';
422 + commonOpts = p: "--verbose --show-cache ${optionalString (!p.enforce) "--complain "}${p.profile}";
423 + in {
424 + Type = "oneshot";
425 + RemainAfterExit = "yes";
426 + ExecStartPre = "${pkgs.apparmor-utils}/bin/aa-teardown";
427 + ExecStart = mapAttrsToList (n: p: "${pkgs.apparmor-parser}/bin/apparmor_parser --add ${commonOpts p}") enabledPolicies;
428 + ExecStartPost = optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
429 + ExecReload =
430 + # Add or replace into the kernel profiles in enabledPolicies
431 + # (because AppArmor can do that without stopping the processes already confined).
432 + mapAttrsToList (n: p: "${pkgs.apparmor-parser}/bin/apparmor_parser --replace ${commonOpts p}") enabledPolicies ++
433 + # Remove from the kernel any profile whose name is not
434 + # one of the names within the content of the profiles in enabledPolicies
435 + # (indirectly read from /etc/apparmor.d/*, without recursing into sub-directory).
436 + # Note that this does not remove profiles dynamically generated by libvirt.
437 + [ "${pkgs.apparmor-utils}/bin/aa-remove-unknown" ] ++
438 + # Optionaly kill the processes which are unconfined but now have a profile loaded
439 + # (because AppArmor can only start to confine new processes).
440 + optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
441 + ExecStop = "${pkgs.apparmor-utils}/bin/aa-teardown";
442 + CacheDirectory = [ "apparmor" "apparmor/logprof" ];
443 + CacheDirectoryMode = "0700";
444 + };
445 + };
446 + };
447 +
448 + meta.maintainers = with maintainers; [ julm ];
449 }
450 diff --git a/nixos/modules/security/apparmor/includes.nix b/nixos/modules/security/apparmor/includes.nix
451 new file mode 100644
452 index 00000000000..e3dd410b3bb
453 --- /dev/null
454 +++ b/nixos/modules/security/apparmor/includes.nix
455 @@ -0,0 +1,317 @@
456 +{ config, lib, pkgs, ... }:
457 +let
458 + inherit (builtins) attrNames hasAttr isAttrs;
459 + inherit (lib) getLib;
460 + inherit (config.environment) etc;
461 + # Utility to generate an AppArmor rule
462 + # only when the given path exists in config.environment.etc
463 + etcRule = arg:
464 + let go = { path ? null, mode ? "r", trail ? "" }:
465 + lib.optionalString (hasAttr path etc)
466 + "${mode} ${config.environment.etc.${path}.source}${trail},";
467 + in if isAttrs arg
468 + then go arg
469 + else go { path = arg; };
470 +in
471 +{
472 +# FIXME: most of the etcRule calls below have been
473 +# written systematically by converting from apparmor-profiles's profiles
474 +# without testing nor deep understanding of their uses,
475 +# and thus may need more rules or can have less rules;
476 +# this remains to be determined case by case,
477 +# some may even be completely useless.
478 +config.security.apparmor.includes = {
479 + # This one is included by <tunables/global>
480 + # which is usualy included before any profile.
481 + "abstractions/tunables/alias" = ''
482 + alias /bin -> /run/current-system/sw/bin,
483 + alias /lib/modules -> /run/current-system/kernel/lib/modules,
484 + alias /sbin -> /run/current-system/sw/sbin,
485 + alias /usr -> /run/current-system/sw,
486 + '';
487 + "abstractions/audio" = ''
488 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/audio"
489 + '' + lib.concatMapStringsSep "\n" etcRule [
490 + "asound.conf"
491 + "esound/esd.conf"
492 + "libao.conf"
493 + { path = "pulse"; trail = "/"; }
494 + { path = "pulse"; trail = "/**"; }
495 + { path = "sound"; trail = "/"; }
496 + { path = "sound"; trail = "/**"; }
497 + { path = "alsa/conf.d"; trail = "/"; }
498 + { path = "alsa/conf.d"; trail = "/*"; }
499 + "openal/alsoft.conf"
500 + "wildmidi/wildmidi.conf"
501 + ];
502 + "abstractions/authentication" = ''
503 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/authentication"
504 + # Defined in security.pam
505 + include <abstractions/pam>
506 + '' + lib.concatMapStringsSep "\n" etcRule [
507 + "nologin"
508 + "securetty"
509 + { path = "security"; trail = "/*"; }
510 + "shadow"
511 + "gshadow"
512 + "pwdb.conf"
513 + "default/passwd"
514 + "login.defs"
515 + ];
516 + "abstractions/base" = ''
517 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/base"
518 + r ${pkgs.stdenv.cc.libc}/share/locale/**,
519 + r ${pkgs.stdenv.cc.libc}/share/locale.alias,
520 + ${lib.optionalString (pkgs.glibcLocales != null) "r ${pkgs.glibcLocales}/lib/locale/locale-archive,"}
521 + ${etcRule "localtime"}
522 + r ${pkgs.tzdata}/share/zoneinfo/**,
523 + r ${pkgs.stdenv.cc.libc}/share/i18n/**,
524 + '';
525 + "abstractions/bash" = ''
526 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/bash"
527 +
528 + # bash inspects filesystems at startup
529 + # and /etc/mtab is linked to /proc/mounts
530 + @{PROC}/mounts
531 +
532 + # system-wide bash configuration
533 + '' + lib.concatMapStringsSep "\n" etcRule [
534 + "profile.dos"
535 + "profile"
536 + "profile.d"
537 + { path = "profile.d"; trail = "/*"; }
538 + "bashrc"
539 + "bash.bashrc"
540 + "bash.bashrc.local"
541 + "bash_completion"
542 + "bash_completion.d"
543 + { path = "bash_completion.d"; trail = "/*"; }
544 + # bash relies on system-wide readline configuration
545 + "inputrc"
546 + # run out of /etc/bash.bashrc
547 + "DIR_COLORS"
548 + ];
549 + "abstractions/consoles" = ''
550 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/consoles"
551 + '';
552 + "abstractions/cups-client" = ''
553 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/cpus-client"
554 + ${etcRule "cups/cups-client.conf"}
555 + '';
556 + "abstractions/dbus-session-strict" = ''
557 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/dbus-session-strict"
558 + ${etcRule "machine-id"}
559 + '';
560 + "abstractions/dconf" = ''
561 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/dconf"
562 + ${etcRule { path = "dconf"; trail = "/**"; }}
563 + '';
564 + "abstractions/dri-common" = ''
565 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/dri-common"
566 + ${etcRule "drirc"}
567 + '';
568 + # The config.fonts.fontconfig NixOS module adds many files to /etc/fonts/
569 + # by symlinking them but without exporting them outside of its NixOS module,
570 + # those are therefore added there to this "abstractions/fonts".
571 + "abstractions/fonts" = ''
572 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/fonts"
573 + ${etcRule { path = "fonts"; trail = "/**"; }}
574 + '';
575 + "abstractions/gnome" = ''
576 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/gnome"
577 + include <abstractions/fonts>
578 + '' + lib.concatMapStringsSep "\n" etcRule [
579 + { path = "gnome"; trail = "/gtkrc*"; }
580 + { path = "gtk"; trail = "/*"; }
581 + { path = "gtk-2.0"; trail = "/*"; }
582 + { path = "gtk-3.0"; trail = "/*"; }
583 + "orbitrc"
584 + { path = "pango"; trail = "/*"; }
585 + { path = "/etc/gnome-vfs-2.0"; trail = "/modules/"; }
586 + { path = "/etc/gnome-vfs-2.0"; trail = "/modules/*"; }
587 + "papersize"
588 + { path = "cups"; trail = "/lpoptions"; }
589 + { path = "gnome"; trail = "/defaults.list"; }
590 + { path = "xdg"; trail = "/{,*-}mimeapps.list"; }
591 + "xdg/mimeapps.list"
592 + ];
593 + "abstractions/kde" = ''
594 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/kde"
595 + '' + lib.concatMapStringsSep "\n" etcRule [
596 + { path = "qt3"; trail = "/kstylerc"; }
597 + { path = "qt3"; trail = "/qt_plugins_3.3rc"; }
598 + { path = "qt3"; trail = "/qtrc"; }
599 + "kderc"
600 + { path = "kde3"; trail = "/*"; }
601 + "kde4rc"
602 + { path = "xdg"; trail = "/kdeglobals"; }
603 + { path = "xdg"; trail = "/Trolltech.conf"; }
604 + ];
605 + "abstractions/kerberosclient" = ''
606 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/kerberosclient"
607 + '' + lib.concatMapStringsSep "\n" etcRule [
608 + { path = "krb5.keytab"; mode="rk"; }
609 + "krb5.conf"
610 + "krb5.conf.d"
611 + { path = "krb5.conf.d"; trail = "/*"; }
612 +
613 + # config files found via strings on libs
614 + "krb.conf"
615 + "krb.realms"
616 + "srvtab"
617 + ];
618 + "abstractions/ldapclient" = ''
619 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/ldapclient"
620 + '' + lib.concatMapStringsSep "\n" etcRule [
621 + "ldap.conf"
622 + "ldap.secret"
623 + { path = "openldap"; trail = "/*"; }
624 + { path = "openldap"; trail = "/cacerts/*"; }
625 + { path = "sasl2"; trail = "/*"; }
626 + ];
627 + "abstractions/likewise" = ''
628 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/likewise"
629 + '';
630 + "abstractions/mdns" = ''
631 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/mdns"
632 + ${etcRule "nss_mdns.conf"}
633 + '';
634 + "abstractions/nameservice" = ''
635 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nameservice"
636 +
637 + # Many programs wish to perform nameservice-like operations, such as
638 + # looking up users by name or id, groups by name or id, hosts by name
639 + # or IP, etc. These operations may be performed through files, dns,
640 + # NIS, NIS+, LDAP, hesiod, wins, etc. Allow them all here.
641 + mr ${getLib pkgs.nss}/lib/libnss_*.so*,
642 + mr ${getLib pkgs.nss}/lib64/libnss_*.so*,
643 + '' + lib.concatMapStringsSep "\n" etcRule [
644 + "group"
645 + "host.conf"
646 + "hosts"
647 + "nsswitch.conf"
648 + "gai.conf"
649 + "passwd"
650 + "protocols"
651 +
652 + # libtirpc (used for NIS/YP login) needs this
653 + "netconfig"
654 +
655 + "resolv.conf"
656 +
657 + { path = "samba"; trail = "/lmhosts"; }
658 + "services"
659 +
660 + "default/nss"
661 +
662 + # libnl-3-200 via libnss-gw-name
663 + { path = "libnl"; trail = "/classid"; }
664 + { path = "libnl-3"; trail = "/classid"; }
665 + ];
666 + "abstractions/nis" = ''
667 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nis"
668 + '';
669 + "abstractions/nvidia" = ''
670 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nvidia"
671 + ${etcRule "vdpau_wrapper.cfg"}
672 + '';
673 + "abstractions/opencl-common" = ''
674 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/opencl-common"
675 + ${etcRule { path = "OpenCL"; trail = "/**"; }}
676 + '';
677 + "abstractions/opencl-mesa" = ''
678 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/opencl-mesa"
679 + ${etcRule "default/drirc"}
680 + '';
681 + "abstractions/openssl" = ''
682 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/openssl"
683 + ${etcRule { path = "ssl"; trail = "/openssl.cnf"; }}
684 + '';
685 + "abstractions/p11-kit" = ''
686 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/p11-kit"
687 + '' + lib.concatMapStringsSep "\n" etcRule [
688 + { path = "pkcs11"; trail = "/"; }
689 + { path = "pkcs11"; trail = "/pkcs11.conf"; }
690 + { path = "pkcs11"; trail = "/modules/"; }
691 + { path = "pkcs11"; trail = "/modules/*"; }
692 + ];
693 + "abstractions/perl" = ''
694 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/perl"
695 + ${etcRule { path = "perl"; trail = "/**"; }}
696 + '';
697 + "abstractions/php" = ''
698 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/php"
699 + '' + lib.concatMapStringsSep "\n" etcRule [
700 + { path = "php"; trail = "/**/"; }
701 + { path = "php5"; trail = "/**/"; }
702 + { path = "php7"; trail = "/**/"; }
703 + { path = "php"; trail = "/**.ini"; }
704 + { path = "php5"; trail = "/**.ini"; }
705 + { path = "php7"; trail = "/**.ini"; }
706 + ];
707 + "abstractions/postfix-common" = ''
708 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/postfix-common"
709 + '' + lib.concatMapStringsSep "\n" etcRule [
710 + "mailname"
711 + { path = "postfix"; trail = "/*.cf"; }
712 + "postfix/main.cf"
713 + "postfix/master.cf"
714 + ];
715 + "abstractions/python" = ''
716 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/python"
717 + '';
718 + "abstractions/qt5" = ''
719 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/qt5"
720 + '' + lib.concatMapStringsSep "\n" etcRule [
721 + { path = "xdg"; trail = "/QtProject/qtlogging.ini"; }
722 + { path = "xdg/QtProject"; trail = "/qtlogging.ini"; }
723 + "xdg/QtProject/qtlogging.ini"
724 + ];
725 + "abstractions/samba" = ''
726 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/samba"
727 + ${etcRule { path = "samba"; trail = "/*"; }}
728 + '';
729 + "abstractions/ssl_certs" = ''
730 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/ssl_certs"
731 +
732 + # For the NixOS module: security.acme
733 + r /var/lib/acme/*/cert.pem,
734 + r /var/lib/acme/*/chain.pem,
735 + r /var/lib/acme/*/fullchain.pem,
736 +
737 + '' + lib.concatMapStringsSep "\n" etcRule [
738 + "ssl/certs/ca-certificates.crt"
739 + "ssl/certs/ca-bundle.crt"
740 + "pki/tls/certs/ca-bundle.crt"
741 +
742 + { path = "ssl/trust"; trail = "/"; }
743 + { path = "ssl/trust"; trail = "/*"; }
744 + { path = "ssl/trust/anchors"; trail = "/"; }
745 + { path = "ssl/trust/anchors"; trail = "/**"; }
746 + { path = "pki/trust"; trail = "/"; }
747 + { path = "pki/trust"; trail = "/*"; }
748 + { path = "pki/trust/anchors"; trail = "/"; }
749 + { path = "pki/trust/anchors"; trail = "/**"; }
750 + ];
751 + "abstractions/ssl_keys" = ''
752 + # security.acme NixOS module
753 + r /var/lib/acme/*/full.pem,
754 + r /var/lib/acme/*/key.pem,
755 + '';
756 + "abstractions/vulkan" = ''
757 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/vulkan"
758 + ${etcRule { path = "vulkan/icd.d"; trail = "/"; }}
759 + ${etcRule { path = "vulkan/icd.d"; trail = "/*.json"; }}
760 + '';
761 + "abstractions/winbind" = ''
762 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/winbind"
763 + ${etcRule { path = "samba"; trail = "/smb.conf"; }}
764 + ${etcRule { path = "samba"; trail = "/dhcp.conf"; }}
765 + '';
766 + "abstractions/X" = ''
767 + include "${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/X"
768 + ${etcRule { path = "X11/cursors"; trail = "/"; }}
769 + ${etcRule { path = "X11/cursors"; trail = "/**"; }}
770 + '';
771 +};
772 +}
773 diff --git a/nixos/modules/security/apparmor/profiles.nix b/nixos/modules/security/apparmor/profiles.nix
774 new file mode 100644
775 index 00000000000..8eb630b5a48
776 --- /dev/null
777 +++ b/nixos/modules/security/apparmor/profiles.nix
778 @@ -0,0 +1,11 @@
779 +{ config, lib, pkgs, ... }:
780 +let apparmor = config.security.apparmor; in
781 +{
782 +config.security.apparmor.packages = [ pkgs.apparmor-profiles ];
783 +config.security.apparmor.policies."bin.ping".profile = lib.mkIf apparmor.policies."bin.ping".enable ''
784 + include "${pkgs.iputils.apparmor}/bin.ping"
785 + include "${pkgs.inetutils.apparmor}/bin.ping"
786 + # Note that including those two profiles in the same profile
787 + # would not work if the second one were to re-include <tunables/global>.
788 +'';
789 +}
790 diff --git a/nixos/modules/security/misc.nix b/nixos/modules/security/misc.nix
791 index d51dbbb77f7..e7abc1e0d59 100644
792 --- a/nixos/modules/security/misc.nix
793 +++ b/nixos/modules/security/misc.nix
794 @@ -7,6 +7,10 @@ with lib;
795 maintainers = [ maintainers.joachifm ];
796 };
797
798 + imports = [
799 + (lib.mkRenamedOptionModule [ "security" "virtualization" "flushL1DataCache" ] [ "security" "virtualisation" "flushL1DataCache" ])
800 + ];
801 +
802 options = {
803 security.allowUserNamespaces = mkOption {
804 type = types.bool;
805 diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
806 index 103cf205012..1c49131d789 100644
807 --- a/nixos/modules/security/pam.nix
808 +++ b/nixos/modules/security/pam.nix
809 @@ -895,6 +895,81 @@ in
810 runuser-l = { rootOK = true; unixAuth = false; };
811 };
812
813 + security.apparmor.includes."abstractions/pam" = let
814 + isEnabled = test: fold or false (map test (attrValues config.security.pam.services));
815 + in
816 + lib.concatMapStringsSep "\n"
817 + (name: "r ${config.environment.etc."pam.d/${name}".source},")
818 + (attrNames config.security.pam.services) +
819 + ''
820 + mr ${getLib pkgs.pam}/lib/security/pam_filter/*,
821 + mr ${getLib pkgs.pam}/lib/security/pam_*.so,
822 + r ${getLib pkgs.pam}/lib/security/,
823 + '' +
824 + optionalString use_ldap ''
825 + mr ${pam_ldap}/lib/security/pam_ldap.so,
826 + '' +
827 + optionalString config.services.sssd.enable ''
828 + mr ${pkgs.sssd}/lib/security/pam_sss.so,
829 + '' +
830 + optionalString config.krb5.enable ''
831 + mr ${pam_krb5}/lib/security/pam_krb5.so,
832 + mr ${pam_ccreds}/lib/security/pam_ccreds.so,
833 + '' +
834 + optionalString (isEnabled (cfg: cfg.googleOsLoginAccountVerification)) ''
835 + mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so,
836 + mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_admin.so,
837 + '' +
838 + optionalString (isEnabled (cfg: cfg.googleOsLoginAuthentication)) ''
839 + mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so,
840 + '' +
841 + optionalString (config.security.pam.enableSSHAgentAuth
842 + && isEnabled (cfg: cfg.sshAgentAuth)) ''
843 + mr ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so,
844 + '' +
845 + optionalString (isEnabled (cfg: cfg.fprintAuth)) ''
846 + mr ${pkgs.fprintd}/lib/security/pam_fprintd.so,
847 + '' +
848 + optionalString (isEnabled (cfg: cfg.u2fAuth)) ''
849 + mr ${pkgs.pam_u2f}/lib/security/pam_u2f.so,
850 + '' +
851 + optionalString (isEnabled (cfg: cfg.usbAuth)) ''
852 + mr ${pkgs.pam_usb}/lib/security/pam_usb.so,
853 + '' +
854 + optionalString (isEnabled (cfg: cfg.oathAuth)) ''
855 + "mr ${pkgs.oathToolkit}/lib/security/pam_oath.so,
856 + '' +
857 + optionalString (isEnabled (cfg: cfg.yubicoAuth)) ''
858 + mr ${pkgs.yubico-pam}/lib/security/pam_yubico.so,
859 + '' +
860 + optionalString (isEnabled (cfg: cfg.duoSecurity.enable)) ''
861 + mr ${pkgs.duo-unix}/lib/security/pam_duo.so,
862 + '' +
863 + optionalString (isEnabled (cfg: cfg.otpwAuth)) ''
864 + mr ${pkgs.otpw}/lib/security/pam_otpw.so,
865 + '' +
866 + optionalString config.security.pam.enableEcryptfs ''
867 + mr ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so,
868 + '' +
869 + optionalString (isEnabled (cfg: cfg.pamMount)) ''
870 + mr ${pkgs.pam_mount}/lib/security/pam_mount.so,
871 + '' +
872 + optionalString (isEnabled (cfg: cfg.enableGnomeKeyring)) ''
873 + mr ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so,
874 + '' +
875 + optionalString (isEnabled (cfg: cfg.startSession)) ''
876 + mr ${pkgs.systemd}/lib/security/pam_systemd.so,
877 + '' +
878 + optionalString (isEnabled (cfg: cfg.enableAppArmor)
879 + && config.security.apparmor.enable) ''
880 + mr ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so,
881 + '' +
882 + optionalString (isEnabled (cfg: cfg.enableKwallet)) ''
883 + mr ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so,
884 + '' +
885 + optionalString config.virtualisation.lxc.lxcfs.enable ''
886 + mr ${pkgs.lxc}/lib/security/pam_cgfs.so
887 + '';
888 };
889
890 }
891 diff --git a/nixos/modules/security/wrappers/default.nix b/nixos/modules/security/wrappers/default.nix
892 index 3cbf22fea7a..1e65f451515 100644
893 --- a/nixos/modules/security/wrappers/default.nix
894 +++ b/nixos/modules/security/wrappers/default.nix
895 @@ -171,6 +171,14 @@ in
896 export PATH="${wrapperDir}:$PATH"
897 '';
898
899 + security.apparmor.includes."nixos/security.wrappers" = ''
900 + include "${pkgs.apparmorRulesFromClosure { name="security.wrappers"; } [
901 + securityWrapper
902 + pkgs.stdenv.cc.cc
903 + pkgs.stdenv.cc.libc
904 + ]}"
905 + '';
906 +
907 ###### setcap activation script
908 system.activationScripts.wrappers =
909 lib.stringAfter [ "specialfs" "users" ]
910 diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
911 index 7bec073e26f..779924a65a8 100644
912 --- a/nixos/modules/services/torrent/transmission.nix
913 +++ b/nixos/modules/services/torrent/transmission.nix
914 @@ -5,17 +5,22 @@ with lib;
915 let
916 cfg = config.services.transmission;
917 inherit (config.environment) etc;
918 - apparmor = config.security.apparmor.enable;
919 + apparmor = config.security.apparmor;
920 rootDir = "/run/transmission";
921 - homeDir = "/var/lib/transmission";
922 settingsDir = ".config/transmission-daemon";
923 downloadsDir = "Downloads";
924 incompleteDir = ".incomplete";
925 watchDir = "watchdir";
926 - # TODO: switch to configGen.json once RFC0042 is implemented
927 - settingsFile = pkgs.writeText "settings.json" (builtins.toJSON cfg.settings);
928 + settingsFormat = pkgs.formats.json {};
929 + settingsFile = settingsFormat.generate "settings.json" cfg.settings;
930 in
931 {
932 + imports = [
933 + (mkRenamedOptionModule ["services" "transmission" "port"]
934 + ["services" "transmission" "settings" "rpc-port"])
935 + (mkAliasOptionModule ["services" "transmission" "openFirewall"]
936 + ["services" "transmission" "openPeerPorts"])
937 + ];
938 options = {
939 services.transmission = {
940 enable = mkEnableOption ''the headless Transmission BitTorrent daemon.
941 @@ -24,48 +29,141 @@ in
942 transmission-remote, the WebUI (http://127.0.0.1:9091/ by default),
943 or other clients like stig or tremc.
944
945 - Torrents are downloaded to ${homeDir}/${downloadsDir} by default and are
946 + Torrents are downloaded to <xref linkend="opt-services.transmission.home"/>/${downloadsDir} by default and are
947 accessible to users in the "transmission" group'';
948
949 - settings = mkOption rec {
950 - # TODO: switch to types.config.json as prescribed by RFC0042 once it's implemented
951 - type = types.attrs;
952 - apply = recursiveUpdate default;
953 - default =
954 - {
955 - download-dir = "${cfg.home}/${downloadsDir}";
956 - incomplete-dir = "${cfg.home}/${incompleteDir}";
957 - incomplete-dir-enabled = true;
958 - watch-dir = "${cfg.home}/${watchDir}";
959 - watch-dir-enabled = false;
960 - message-level = 1;
961 - peer-port = 51413;
962 - peer-port-random-high = 65535;
963 - peer-port-random-low = 49152;
964 - peer-port-random-on-start = false;
965 - rpc-bind-address = "127.0.0.1";
966 - rpc-port = 9091;
967 - script-torrent-done-enabled = false;
968 - script-torrent-done-filename = "";
969 - umask = 2; # 0o002 in decimal as expected by Transmission
970 - utp-enabled = true;
971 - };
972 - example =
973 - {
974 - download-dir = "/srv/torrents/";
975 - incomplete-dir = "/srv/torrents/.incomplete/";
976 - incomplete-dir-enabled = true;
977 - rpc-whitelist = "127.0.0.1,192.168.*.*";
978 - };
979 + settings = mkOption {
980 description = ''
981 - Attribute set whose fields overwrites fields in
982 + Settings whose options overwrite fields in
983 <literal>.config/transmission-daemon/settings.json</literal>
984 - (each time the service starts). String values must be quoted, integer and
985 - boolean values must not.
986 + (each time the service starts).
987
988 See <link xlink:href="https://github.com/transmission/transmission/wiki/Editing-Configuration-Files">Transmission's Wiki</link>
989 - for documentation.
990 + for documentation of settings not explicitely covered by this module.
991 '';
992 + default = {};
993 + type = types.submodule {
994 + freeformType = settingsFormat.type;
995 + options.download-dir = mkOption {
996 + type = types.path;
997 + default = "${cfg.home}/${downloadsDir}";
998 + description = "Directory where to download torrents.";
999 + };
1000 + options.incomplete-dir = mkOption {
1001 + type = types.path;
1002 + default = "${cfg.home}/${incompleteDir}";
1003 + description = ''
1004 + When enabled with
1005 + services.transmission.home
1006 + <xref linkend="opt-services.transmission.settings.incomplete-dir-enabled"/>,
1007 + new torrents will download the files to this directory.
1008 + When complete, the files will be moved to download-dir
1009 + <xref linkend="opt-services.transmission.settings.download-dir"/>.
1010 + '';
1011 + };
1012 + options.incomplete-dir-enabled = mkOption {
1013 + type = types.bool;
1014 + default = true;
1015 + description = "";
1016 + };
1017 + options.message-level = mkOption {
1018 + type = types.ints.between 0 2;
1019 + default = 2;
1020 + description = "Set verbosity of transmission messages.";
1021 + };
1022 + options.peer-port = mkOption {
1023 + type = types.port;
1024 + default = 51413;
1025 + description = "The peer port to listen for incoming connections.";
1026 + };
1027 + options.peer-port-random-high = mkOption {
1028 + type = types.port;
1029 + default = 65535;
1030 + description = ''
1031 + The maximum peer port to listen to for incoming connections
1032 + when <xref linkend="opt-services.transmission.settings.peer-port-random-on-start"/> is enabled.
1033 + '';
1034 + };
1035 + options.peer-port-random-low = mkOption {
1036 + type = types.port;
1037 + default = 65535;
1038 + description = ''
1039 + The minimal peer port to listen to for incoming connections
1040 + when <xref linkend="opt-services.transmission.settings.peer-port-random-on-start"/> is enabled.
1041 + '';
1042 + };
1043 + options.peer-port-random-on-start = mkOption {
1044 + type = types.bool;
1045 + default = false;
1046 + description = "Randomize the peer port.";
1047 + };
1048 + options.rpc-bind-address = mkOption {
1049 + type = types.str;
1050 + default = "127.0.0.1";
1051 + example = "0.0.0.0";
1052 + description = ''
1053 + Where to listen for RPC connections.
1054 + Use \"0.0.0.0\" to listen on all interfaces.
1055 + '';
1056 + };
1057 + options.rpc-port = mkOption {
1058 + type = types.port;
1059 + default = 9091;
1060 + description = "The RPC port to listen to.";
1061 + };
1062 + options.script-torrent-done-enabled = mkOption {
1063 + type = types.bool;
1064 + default = false;
1065 + description = ''
1066 + Whether to run
1067 + <xref linkend="opt-services.transmission.settings.script-torrent-done-filename"/>
1068 + at torrent completion.
1069 + '';
1070 + };
1071 + options.script-torrent-done-filename = mkOption {
1072 + type = types.nullOr types.path;
1073 + default = null;
1074 + description = "Executable to be run at torrent completion.";
1075 + };
1076 + options.umask = mkOption {
1077 + type = types.int;
1078 + default = 2;
1079 + description = ''
1080 + Sets transmission's file mode creation mask.
1081 + See the umask(2) manpage for more information.
1082 + Users who want their saved torrents to be world-writable
1083 + may want to set this value to 0.
1084 + Bear in mind that the json markup language only accepts numbers in base 10,
1085 + so the standard umask(2) octal notation "022" is written in settings.json as 18.
1086 + '';
1087 + };
1088 + options.utp-enabled = mkOption {
1089 + type = types.bool;
1090 + default = true;
1091 + description = ''
1092 + Whether to enable <link xlink:href="http://en.wikipedia.org/wiki/Micro_Transport_Protocol">Micro Transport Protocol (µTP)</link>.
1093 + '';
1094 + };
1095 + options.watch-dir = mkOption {
1096 + type = types.path;
1097 + default = "${cfg.home}/${watchDir}";
1098 + description = "Watch a directory for torrent files and add them to transmission.";
1099 + };
1100 + options.watch-dir-enabled = mkOption {
1101 + type = types.bool;
1102 + default = false;
1103 + description = ''Whether to enable the
1104 + <xref linkend="opt-services.transmission.settings.watch-dir"/>.
1105 + '';
1106 + };
1107 + options.trash-original-torrent-files = mkOption {
1108 + type = types.bool;
1109 + default = false;
1110 + description = ''Whether to delete torrents added from the
1111 + <xref linkend="opt-services.transmission.settings.watch-dir"/>.
1112 + '';
1113 + };
1114 + };
1115 };
1116
1117 downloadDirPermissions = mkOption {
1118 @@ -74,31 +172,22 @@ in
1119 example = "775";
1120 description = ''
1121 The permissions set by <literal>systemd.activationScripts.transmission-daemon</literal>
1122 - on the directories <link linkend="opt-services.transmission.settings">settings.download-dir</link>
1123 - and <link linkend="opt-services.transmission.settings">settings.incomplete-dir</link>.
1124 + on the directories <xref linkend="opt-services.transmission.settings.download-dir"/>
1125 + and <xref linkend="opt-services.transmission.settings.incomplete-dir"/>.
1126 Note that you may also want to change
1127 - <link linkend="opt-services.transmission.settings">settings.umask</link>.
1128 - '';
1129 - };
1130 -
1131 - port = mkOption {
1132 - type = types.port;
1133 - description = ''
1134 - TCP port number to run the RPC/web interface.
1135 -
1136 - If instead you want to change the peer port,
1137 - use <link linkend="opt-services.transmission.settings">settings.peer-port</link>
1138 - or <link linkend="opt-services.transmission.settings">settings.peer-port-random-on-start</link>.
1139 + <xref linkend="opt-services.transmission.settings.umask"/>.
1140 '';
1141 };
1142
1143 home = mkOption {
1144 type = types.path;
1145 - default = homeDir;
1146 + default = "/var/lib/transmission";
1147 description = ''
1148 The directory where Transmission will create <literal>${settingsDir}</literal>.
1149 - as well as <literal>${downloadsDir}/</literal> unless <link linkend="opt-services.transmission.settings">settings.download-dir</link> is changed,
1150 - and <literal>${incompleteDir}/</literal> unless <link linkend="opt-services.transmission.settings">settings.incomplete-dir</link> is changed.
1151 + as well as <literal>${downloadsDir}/</literal> unless
1152 + <xref linkend="opt-services.transmission.settings.download-dir"/> is changed,
1153 + and <literal>${incompleteDir}/</literal> unless
1154 + <xref linkend="opt-services.transmission.settings.incomplete-dir"/> is changed.
1155 '';
1156 };
1157
1158 @@ -119,19 +208,22 @@ in
1159 description = ''
1160 Path to a JSON file to be merged with the settings.
1161 Useful to merge a file which is better kept out of the Nix store
1162 - because it contains sensible data like <link linkend="opt-services.transmission.settings">settings.rpc-password</link>.
1163 + because it contains sensible data like
1164 + <xref linkend="opt-services.transmission.settings.rpc-password"/>.
1165 '';
1166 default = "/dev/null";
1167 example = "/var/lib/secrets/transmission/settings.json";
1168 };
1169
1170 - openFirewall = mkEnableOption "opening of the peer port(s) in the firewall";
1171 + openPeerPorts = mkEnableOption "opening of the peer port(s) in the firewall";
1172 +
1173 + openRPCPort = mkEnableOption "opening of the RPC port in the firewall";
1174
1175 performanceNetParameters = mkEnableOption ''tweaking of kernel parameters
1176 to open many more connections at the same time.
1177
1178 Note that you may also want to increase
1179 - <link linkend="opt-services.transmission.settings">settings.peer-limit-global</link>.
1180 + <xref linkend="opt-services.transmission.settings.peer-limit-global"/>.
1181 And be aware that these settings are quite aggressive
1182 and might not suite your regular desktop use.
1183 For instance, SSH sessions may time out more easily'';
1184 @@ -152,40 +244,14 @@ in
1185 install -d -m '${cfg.downloadDirPermissions}' -o '${cfg.user}' -g '${cfg.group}' '${cfg.settings.download-dir}'
1186 '' + optionalString cfg.settings.incomplete-dir-enabled ''
1187 install -d -m '${cfg.downloadDirPermissions}' -o '${cfg.user}' -g '${cfg.group}' '${cfg.settings.incomplete-dir}'
1188 + '' + optionalString cfg.settings.watch-dir-enabled ''
1189 + install -d -m '${cfg.downloadDirPermissions}' -o '${cfg.user}' -g '${cfg.group}' '${cfg.settings.watch-dir}'
1190 '';
1191
1192 - assertions = [
1193 - { assertion = builtins.match "^/.*" cfg.home != null;
1194 - message = "`services.transmission.home' must be an absolute path.";
1195 - }
1196 - { assertion = types.path.check cfg.settings.download-dir;
1197 - message = "`services.transmission.settings.download-dir' must be an absolute path.";
1198 - }
1199 - { assertion = types.path.check cfg.settings.incomplete-dir;
1200 - message = "`services.transmission.settings.incomplete-dir' must be an absolute path.";
1201 - }
1202 - { assertion = types.path.check cfg.settings.watch-dir;
1203 - message = "`services.transmission.settings.watch-dir' must be an absolute path.";
1204 - }
1205 - { assertion = cfg.settings.script-torrent-done-filename == "" || types.path.check cfg.settings.script-torrent-done-filename;
1206 - message = "`services.transmission.settings.script-torrent-done-filename' must be an absolute path.";
1207 - }
1208 - { assertion = types.port.check cfg.settings.rpc-port;
1209 - message = "${toString cfg.settings.rpc-port} is not a valid port number for `services.transmission.settings.rpc-port`.";
1210 - }
1211 - # In case both port and settings.rpc-port are explicitely defined: they must be the same.
1212 - { assertion = !options.services.transmission.port.isDefined || cfg.port == cfg.settings.rpc-port;
1213 - message = "`services.transmission.port' is not equal to `services.transmission.settings.rpc-port'";
1214 - }
1215 - ];
1216 -
1217 - services.transmission.settings =
1218 - optionalAttrs options.services.transmission.port.isDefined { rpc-port = cfg.port; };
1219 -
1220 systemd.services.transmission = {
1221 description = "Transmission BitTorrent Service";
1222 - after = [ "network.target" ] ++ optional apparmor "apparmor.service";
1223 - requires = optional apparmor "apparmor.service";
1224 + after = [ "network.target" ] ++ optional apparmor.enable "apparmor.service";
1225 + requires = optional apparmor.enable "apparmor.service";
1226 wantedBy = [ "multi-user.target" ];
1227 environment.CURL_CA_BUNDLE = etc."ssl/certs/ca-certificates.crt".source;
1228
1229 @@ -226,11 +292,9 @@ in
1230 cfg.settings.download-dir
1231 ] ++
1232 optional cfg.settings.incomplete-dir-enabled
1233 - cfg.settings.incomplete-dir
1234 - ++
1235 - optional cfg.settings.watch-dir-enabled
1236 - cfg.settings.watch-dir
1237 - ;
1238 + cfg.settings.incomplete-dir ++
1239 + optional (cfg.settings.watch-dir-enabled && cfg.settings.trash-original-torrent-files)
1240 + cfg.settings.watch-dir;
1241 BindReadOnlyPaths = [
1242 # No confinement done of /nix/store here like in systemd-confinement.nix,
1243 # an AppArmor profile is provided to get a confinement based upon paths and rights.
1244 @@ -239,8 +303,10 @@ in
1245 "/run"
1246 ] ++
1247 optional (cfg.settings.script-torrent-done-enabled &&
1248 - cfg.settings.script-torrent-done-filename != "")
1249 - cfg.settings.script-torrent-done-filename;
1250 + cfg.settings.script-torrent-done-filename != null)
1251 + cfg.settings.script-torrent-done-filename ++
1252 + optional (cfg.settings.watch-dir-enabled && !cfg.settings.trash-original-torrent-files)
1253 + cfg.settings.watch-dir;
1254 # The following options are only for optimizing:
1255 # systemd-analyze security transmission
1256 AmbientCapabilities = "";
1257 @@ -307,25 +373,28 @@ in
1258 };
1259 });
1260
1261 - networking.firewall = mkIf cfg.openFirewall (
1262 - if cfg.settings.peer-port-random-on-start
1263 - then
1264 - { allowedTCPPortRanges =
1265 - [ { from = cfg.settings.peer-port-random-low;
1266 - to = cfg.settings.peer-port-random-high;
1267 - }
1268 - ];
1269 - allowedUDPPortRanges =
1270 - [ { from = cfg.settings.peer-port-random-low;
1271 - to = cfg.settings.peer-port-random-high;
1272 - }
1273 - ];
1274 - }
1275 - else
1276 - { allowedTCPPorts = [ cfg.settings.peer-port ];
1277 - allowedUDPPorts = [ cfg.settings.peer-port ];
1278 - }
1279 - );
1280 + networking.firewall = mkMerge [
1281 + (mkIf cfg.openPeerPorts (
1282 + if cfg.settings.peer-port-random-on-start
1283 + then
1284 + { allowedTCPPortRanges =
1285 + [ { from = cfg.settings.peer-port-random-low;
1286 + to = cfg.settings.peer-port-random-high;
1287 + }
1288 + ];
1289 + allowedUDPPortRanges =
1290 + [ { from = cfg.settings.peer-port-random-low;
1291 + to = cfg.settings.peer-port-random-high;
1292 + }
1293 + ];
1294 + }
1295 + else
1296 + { allowedTCPPorts = [ cfg.settings.peer-port ];
1297 + allowedUDPPorts = [ cfg.settings.peer-port ];
1298 + }
1299 + ))
1300 + (mkIf cfg.openRPCPort { allowedTCPPorts = [ cfg.settings.rpc-port ]; })
1301 + ];
1302
1303 boot.kernel.sysctl = mkMerge [
1304 # Transmission uses a single UDP socket in order to implement multiple uTP sockets,
1305 @@ -340,113 +409,57 @@ in
1306 # Increase the number of available source (local) TCP and UDP ports to 49151.
1307 # Usual default is 32768 60999, ie. 28231 ports.
1308 # Find out your current usage with: ss -s
1309 - "net.ipv4.ip_local_port_range" = "16384 65535";
1310 + "net.ipv4.ip_local_port_range" = mkDefault "16384 65535";
1311 # Timeout faster generic TCP states.
1312 # Usual default is 600.
1313 # Find out your current usage with: watch -n 1 netstat -nptuo
1314 - "net.netfilter.nf_conntrack_generic_timeout" = 60;
1315 + "net.netfilter.nf_conntrack_generic_timeout" = mkDefault 60;
1316 # Timeout faster established but inactive connections.
1317 # Usual default is 432000.
1318 - "net.netfilter.nf_conntrack_tcp_timeout_established" = 600;
1319 + "net.netfilter.nf_conntrack_tcp_timeout_established" = mkDefault 600;
1320 # Clear immediately TCP states after timeout.
1321 # Usual default is 120.
1322 - "net.netfilter.nf_conntrack_tcp_timeout_time_wait" = 1;
1323 + "net.netfilter.nf_conntrack_tcp_timeout_time_wait" = mkDefault 1;
1324 # Increase the number of trackable connections.
1325 # Usual default is 262144.
1326 # Find out your current usage with: conntrack -C
1327 - "net.netfilter.nf_conntrack_max" = 1048576;
1328 + "net.netfilter.nf_conntrack_max" = mkDefault 1048576;
1329 })
1330 ];
1331
1332 - security.apparmor.profiles = mkIf apparmor [
1333 - (pkgs.writeText "apparmor-transmission-daemon" ''
1334 - include <tunables/global>
1335 + security.apparmor.policies."bin.transmission-daemon".profile = ''
1336 + include "${pkgs.transmission.apparmor}/bin.transmission-daemon"
1337 + '';
1338 + security.apparmor.includes."local/bin.transmission-daemon" = ''
1339 + r ${config.systemd.services.transmission.environment.CURL_CA_BUNDLE},
1340
1341 - ${pkgs.transmission}/bin/transmission-daemon {
1342 - include <abstractions/base>
1343 - include <abstractions/nameservice>
1344 + owner rw ${cfg.home}/${settingsDir}/**,
1345 + rw ${cfg.settings.download-dir}/**,
1346 + ${optionalString cfg.settings.incomplete-dir-enabled ''
1347 + rw ${cfg.settings.incomplete-dir}/**,
1348 + ''}
1349 + ${optionalString cfg.settings.watch-dir-enabled ''
1350 + r${optionalString cfg.settings.trash-original-torrent-files "w"} ${cfg.settings.watch-dir}/**,
1351 + ''}
1352 + profile dirs {
1353 + rw ${cfg.settings.download-dir}/**,
1354 + ${optionalString cfg.settings.incomplete-dir-enabled ''
1355 + rw ${cfg.settings.incomplete-dir}/**,
1356 + ''}
1357 + ${optionalString cfg.settings.watch-dir-enabled ''
1358 + r${optionalString cfg.settings.trash-original-torrent-files "w"} ${cfg.settings.watch-dir}/**,
1359 + ''}
1360 + }
1361
1362 - # NOTE: https://github.com/NixOS/nixpkgs/pull/93457
1363 - # will remove the need for these by fixing <abstractions/base>
1364 - r ${etc."hosts".source},
1365 - r /etc/ld-nix.so.preload,
1366 - ${lib.optionalString (builtins.hasAttr "ld-nix.so.preload" etc) ''
1367 - r ${etc."ld-nix.so.preload".source},
1368 - ${concatMapStrings (p: optionalString (p != "") ("mr ${p},\n"))
1369 - (splitString "\n" config.environment.etc."ld-nix.so.preload".text)}
1370 - ''}
1371 - r ${etc."ssl/certs/ca-certificates.crt".source},
1372 - r ${pkgs.tzdata}/share/zoneinfo/**,
1373 - r ${pkgs.stdenv.cc.libc}/share/i18n/**,
1374 - r ${pkgs.stdenv.cc.libc}/share/locale/**,
1375 -
1376 - mr ${getLib pkgs.stdenv.cc.cc}/lib/*.so*,
1377 - mr ${getLib pkgs.stdenv.cc.libc}/lib/*.so*,
1378 - mr ${getLib pkgs.attr}/lib/libattr*.so*,
1379 - mr ${getLib pkgs.c-ares}/lib/libcares*.so*,
1380 - mr ${getLib pkgs.curl}/lib/libcurl*.so*,
1381 - mr ${getLib pkgs.keyutils}/lib/libkeyutils*.so*,
1382 - mr ${getLib pkgs.libcap}/lib/libcap*.so*,
1383 - mr ${getLib pkgs.libevent}/lib/libevent*.so*,
1384 - mr ${getLib pkgs.libgcrypt}/lib/libgcrypt*.so*,
1385 - mr ${getLib pkgs.libgpgerror}/lib/libgpg-error*.so*,
1386 - mr ${getLib pkgs.libkrb5}/lib/lib*.so*,
1387 - mr ${getLib pkgs.libssh2}/lib/libssh2*.so*,
1388 - mr ${getLib pkgs.lz4}/lib/liblz4*.so*,
1389 - mr ${getLib pkgs.nghttp2}/lib/libnghttp2*.so*,
1390 - mr ${getLib pkgs.openssl}/lib/libcrypto*.so*,
1391 - mr ${getLib pkgs.openssl}/lib/libssl*.so*,
1392 - mr ${getLib pkgs.systemd}/lib/libsystemd*.so*,
1393 - mr ${getLib pkgs.util-linuxMinimal.out}/lib/libblkid.so*,
1394 - mr ${getLib pkgs.util-linuxMinimal.out}/lib/libmount.so*,
1395 - mr ${getLib pkgs.util-linuxMinimal.out}/lib/libuuid.so*,
1396 - mr ${getLib pkgs.xz}/lib/liblzma*.so*,
1397 - mr ${getLib pkgs.zlib}/lib/libz*.so*,
1398 -
1399 - r @{PROC}/sys/kernel/random/uuid,
1400 - r @{PROC}/sys/vm/overcommit_memory,
1401 - # @{pid} is not a kernel variable yet but a regexp
1402 - #r @{PROC}/@{pid}/environ,
1403 - r @{PROC}/@{pid}/mounts,
1404 - rwk /tmp/tr_session_id_*,
1405 - r /run/systemd/resolve/stub-resolv.conf,
1406 -
1407 - r ${pkgs.openssl.out}/etc/**,
1408 - r ${config.systemd.services.transmission.environment.CURL_CA_BUNDLE},
1409 - r ${pkgs.transmission}/share/transmission/**,
1410 -
1411 - owner rw ${cfg.home}/${settingsDir}/**,
1412 - rw ${cfg.settings.download-dir}/**,
1413 - ${optionalString cfg.settings.incomplete-dir-enabled ''
1414 - rw ${cfg.settings.incomplete-dir}/**,
1415 - ''}
1416 - ${optionalString cfg.settings.watch-dir-enabled ''
1417 - rw ${cfg.settings.watch-dir}/**,
1418 - ''}
1419 - profile dirs {
1420 - rw ${cfg.settings.download-dir}/**,
1421 - ${optionalString cfg.settings.incomplete-dir-enabled ''
1422 - rw ${cfg.settings.incomplete-dir}/**,
1423 - ''}
1424 - ${optionalString cfg.settings.watch-dir-enabled ''
1425 - rw ${cfg.settings.watch-dir}/**,
1426 - ''}
1427 - }
1428 -
1429 - ${optionalString (cfg.settings.script-torrent-done-enabled &&
1430 - cfg.settings.script-torrent-done-filename != "") ''
1431 - # Stack transmission_directories profile on top of
1432 - # any existing profile for script-torrent-done-filename
1433 - # FIXME: to be tested as I'm not sure it works well with NoNewPrivileges=
1434 - # https://gitlab.com/apparmor/apparmor/-/wikis/AppArmorStacking#seccomp-and-no_new_privs
1435 - px ${cfg.settings.script-torrent-done-filename} -> &@{dirs},
1436 - ''}
1437 -
1438 - # FIXME: enable customizing using https://github.com/NixOS/nixpkgs/pull/93457
1439 - # include <local/transmission-daemon>
1440 - }
1441 - '')
1442 - ];
1443 + ${optionalString (cfg.settings.script-torrent-done-enabled &&
1444 + cfg.settings.script-torrent-done-filename != null) ''
1445 + # Stack transmission_directories profile on top of
1446 + # any existing profile for script-torrent-done-filename
1447 + # FIXME: to be tested as I'm not sure it works well with NoNewPrivileges=
1448 + # https://gitlab.com/apparmor/apparmor/-/wikis/AppArmorStacking#seccomp-and-no_new_privs
1449 + px ${cfg.settings.script-torrent-done-filename} -> &@{dirs},
1450 + ''}
1451 + '';
1452 };
1453
1454 meta.maintainers = with lib.maintainers; [ julm ];
1455 diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
1456 index f730ec82bdf..3bfb59a8848 100644
1457 --- a/nixos/modules/tasks/network-interfaces.nix
1458 +++ b/nixos/modules/tasks/network-interfaces.nix
1459 @@ -1111,6 +1111,21 @@ in
1460 } else {
1461 ping.source = "${pkgs.iputils.out}/bin/ping";
1462 };
1463 + security.apparmor.policies."bin.ping".profile = lib.mkIf config.security.apparmor.policies."bin.ping".enable (lib.mkAfter ''
1464 + /run/wrappers/bin/ping {
1465 + include <abstractions/base>
1466 + include <nixos/security.wrappers>
1467 + rpx /run/wrappers/wrappers.*/ping,
1468 + }
1469 + /run/wrappers/wrappers.*/ping {
1470 + include <abstractions/base>
1471 + include <nixos/security.wrappers>
1472 + r /run/wrappers/wrappers.*/ping.real,
1473 + mrpx ${config.security.wrappers.ping.source},
1474 + capability net_raw,
1475 + capability setpcap,
1476 + }
1477 + '');
1478
1479 # Set the host and domain names in the activation script. Don't
1480 # clear it if it's not configured in the NixOS configuration,
1481 diff --git a/nixos/modules/virtualisation/lxc.nix b/nixos/modules/virtualisation/lxc.nix
1482 index f484d5ee59a..0f8b22a45df 100644
1483 --- a/nixos/modules/virtualisation/lxc.nix
1484 +++ b/nixos/modules/virtualisation/lxc.nix
1485 @@ -74,9 +74,13 @@ in
1486 systemd.tmpfiles.rules = [ "d /var/lib/lxc/rootfs 0755 root root -" ];
1487
1488 security.apparmor.packages = [ pkgs.lxc ];
1489 - security.apparmor.profiles = [
1490 - "${pkgs.lxc}/etc/apparmor.d/lxc-containers"
1491 - "${pkgs.lxc}/etc/apparmor.d/usr.bin.lxc-start"
1492 - ];
1493 + security.apparmor.policies = {
1494 + "bin.lxc-start".profile = ''
1495 + include ${pkgs.lxc}/etc/apparmor.d/usr.bin.lxc-start
1496 + '';
1497 + "lxc-containers".profile = ''
1498 + include ${pkgs.lxc}/etc/apparmor.d/lxc-containers
1499 + '';
1500 + };
1501 };
1502 }
1503 diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix
1504 index 96e8d68ae50..6b6f4b6e652 100644
1505 --- a/nixos/modules/virtualisation/lxd.nix
1506 +++ b/nixos/modules/virtualisation/lxd.nix
1507 @@ -97,11 +97,17 @@ in {
1508 # does a bunch of unrelated things.
1509 systemd.tmpfiles.rules = [ "d /var/lib/lxc/rootfs 0755 root root -" ];
1510
1511 - security.apparmor.packages = [ cfg.lxcPackage ];
1512 - security.apparmor.profiles = [
1513 - "${cfg.lxcPackage}/etc/apparmor.d/lxc-containers"
1514 - "${cfg.lxcPackage}/etc/apparmor.d/usr.bin.lxc-start"
1515 - ];
1516 + security.apparmor = {
1517 + packages = [ cfg.lxcPackage ];
1518 + policies = {
1519 + "bin.lxc-start".profile = ''
1520 + include ${cfg.lxcPackage}/etc/apparmor.d/usr.bin.lxc-start
1521 + '';
1522 + "lxc-containers".profile = ''
1523 + include ${cfg.lxcPackage}/etc/apparmor.d/lxc-containers
1524 + '';
1525 + };
1526 + };
1527
1528 # TODO: remove once LXD gets proper support for cgroupsv2
1529 # (currently most of the e.g. CPU accounting stuff doesn't work)
1530 diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
1531 index fb45ec1a310..957d052ace1 100644
1532 --- a/nixos/tests/all-tests.nix
1533 +++ b/nixos/tests/all-tests.nix
1534 @@ -25,6 +25,7 @@ in
1535 acme = handleTest ./acme.nix {};
1536 agda = handleTest ./agda.nix {};
1537 ammonite = handleTest ./ammonite.nix {};
1538 + apparmor = handleTest ./apparmor.nix {};
1539 atd = handleTest ./atd.nix {};
1540 avahi = handleTest ./avahi.nix {};
1541 avahi-with-resolved = handleTest ./avahi.nix { networkd = true; };
1542 diff --git a/nixos/tests/apparmor.nix b/nixos/tests/apparmor.nix
1543 new file mode 100644
1544 index 00000000000..c6daa8e67de
1545 --- /dev/null
1546 +++ b/nixos/tests/apparmor.nix
1547 @@ -0,0 +1,82 @@
1548 +import ./make-test-python.nix ({ pkgs, ... } : {
1549 + name = "apparmor";
1550 + meta = with pkgs.lib.maintainers; {
1551 + maintainers = [ julm ];
1552 + };
1553 +
1554 + machine =
1555 + { lib, pkgs, config, ... }:
1556 + with lib;
1557 + {
1558 + security.apparmor.enable = mkDefault true;
1559 + };
1560 +
1561 + testScript =
1562 + ''
1563 + machine.wait_for_unit("multi-user.target")
1564 +
1565 + with subtest("AppArmor profiles are loaded"):
1566 + machine.succeed("systemctl status apparmor.service")
1567 +
1568 + # AppArmor securityfs
1569 + with subtest("AppArmor securityfs is mounted"):
1570 + machine.succeed("mountpoint -q /sys/kernel/security")
1571 + machine.succeed("cat /sys/kernel/security/apparmor/profiles")
1572 +
1573 + # Test apparmorRulesFromClosure by:
1574 + # 1. Prepending a string of the relevant packages' name and version on each line.
1575 + # 2. Sorting according to those strings.
1576 + # 3. Removing those prepended strings.
1577 + # 4. Using `diff` against the expected output.
1578 + with subtest("apparmorRulesFromClosure"):
1579 + machine.succeed(
1580 + "${pkgs.diffutils}/bin/diff ${pkgs.writeText "expected.rules" ''
1581 + mr ${pkgs.bash}/lib/**.so*,
1582 + r ${pkgs.bash},
1583 + r ${pkgs.bash}/etc/**,
1584 + r ${pkgs.bash}/lib/**,
1585 + r ${pkgs.bash}/share/**,
1586 + x ${pkgs.bash}/foo/**,
1587 + mr ${pkgs.glibc}/lib/**.so*,
1588 + r ${pkgs.glibc},
1589 + r ${pkgs.glibc}/etc/**,
1590 + r ${pkgs.glibc}/lib/**,
1591 + r ${pkgs.glibc}/share/**,
1592 + x ${pkgs.glibc}/foo/**,
1593 + mr ${pkgs.libcap}/lib/**.so*,
1594 + r ${pkgs.libcap},
1595 + r ${pkgs.libcap}/etc/**,
1596 + r ${pkgs.libcap}/lib/**,
1597 + r ${pkgs.libcap}/share/**,
1598 + x ${pkgs.libcap}/foo/**,
1599 + mr ${pkgs.libcap.lib}/lib/**.so*,
1600 + r ${pkgs.libcap.lib},
1601 + r ${pkgs.libcap.lib}/etc/**,
1602 + r ${pkgs.libcap.lib}/lib/**,
1603 + r ${pkgs.libcap.lib}/share/**,
1604 + x ${pkgs.libcap.lib}/foo/**,
1605 + mr ${pkgs.libidn2.out}/lib/**.so*,
1606 + r ${pkgs.libidn2.out},
1607 + r ${pkgs.libidn2.out}/etc/**,
1608 + r ${pkgs.libidn2.out}/lib/**,
1609 + r ${pkgs.libidn2.out}/share/**,
1610 + x ${pkgs.libidn2.out}/foo/**,
1611 + mr ${pkgs.libunistring}/lib/**.so*,
1612 + r ${pkgs.libunistring},
1613 + r ${pkgs.libunistring}/etc/**,
1614 + r ${pkgs.libunistring}/lib/**,
1615 + r ${pkgs.libunistring}/share/**,
1616 + x ${pkgs.libunistring}/foo/**,
1617 + ''} ${pkgs.runCommand "actual.rules" { preferLocalBuild = true; } ''
1618 + ${pkgs.gnused}/bin/sed -e 's:^[^ ]* ${builtins.storeDir}/[^,/-]*-\([^/,]*\):\1 \0:' ${
1619 + pkgs.apparmorRulesFromClosure {
1620 + name = "ping";
1621 + additionalRules = ["x $path/foo/**"];
1622 + } [ pkgs.libcap ]
1623 + } |
1624 + ${pkgs.coreutils}/bin/sort -n -k1 |
1625 + ${pkgs.gnused}/bin/sed -e 's:^[^ ]* ::' >$out
1626 + ''}"
1627 + )
1628 + '';
1629 +})
1630 diff --git a/pkgs/applications/networking/p2p/transmission/default.nix b/pkgs/applications/networking/p2p/transmission/default.nix
1631 index 7e8b6b671cd..b2519eb2fa0 100644
1632 --- a/pkgs/applications/networking/p2p/transmission/default.nix
1633 +++ b/pkgs/applications/networking/p2p/transmission/default.nix
1634 @@ -20,6 +20,7 @@
1635 , enableSystemd ? stdenv.isLinux
1636 , enableDaemon ? true
1637 , enableCli ? true
1638 +, apparmorRulesFromClosure
1639 }:
1640
1641 let
1642 @@ -37,6 +38,8 @@ in stdenv.mkDerivation {
1643 fetchSubmodules = true;
1644 };
1645
1646 + outputs = [ "out" "apparmor" ];
1647 +
1648 cmakeFlags =
1649 let
1650 mkFlag = opt: if opt then "ON" else "OFF";
1651 @@ -72,6 +75,30 @@ in stdenv.mkDerivation {
1652
1653 NIX_LDFLAGS = lib.optionalString stdenv.isDarwin "-framework CoreFoundation";
1654
1655 + postInstall = ''
1656 + install -D -m 644 /dev/stdin $apparmor/bin.transmission-daemon <<EOF
1657 + include <tunables/global>
1658 + $out/bin/transmission-daemon {
1659 + include <abstractions/base>
1660 + include <abstractions/nameservice>
1661 + include <abstractions/ssl_certs>
1662 + include "${apparmorRulesFromClosure { name = "transmission-daemon"; } ([
1663 + curl libevent openssl pcre zlib
1664 + ] ++ lib.optionals enableSystemd [ systemd ]
1665 + ++ lib.optionals stdenv.isLinux [ inotify-tools ]
1666 + )}"
1667 + r @{PROC}/sys/kernel/random/uuid,
1668 + r @{PROC}/sys/vm/overcommit_memory,
1669 + r @{PROC}/@{pid}/environ,
1670 + r @{PROC}/@{pid}/mounts,
1671 + rwk /tmp/tr_session_id_*,
1672 + r /run/systemd/resolve/stub-resolv.conf,
1673 +
1674 + include <local/bin.transmission-daemon>
1675 + }
1676 + EOF
1677 + '';
1678 +
1679 meta = {
1680 description = "A fast, easy and free BitTorrent client";
1681 longDescription = ''
1682 diff --git a/pkgs/os-specific/linux/apparmor/default.nix b/pkgs/os-specific/linux/apparmor/default.nix
1683 index 935b5e65b1f..a07cd5070d2 100644
1684 --- a/pkgs/os-specific/linux/apparmor/default.nix
1685 +++ b/pkgs/os-specific/linux/apparmor/default.nix
1686 @@ -10,26 +10,37 @@
1687 , pam
1688 , libnotify
1689 , buildPackages
1690 +, coreutils
1691 +, gnugrep
1692 +, gnused
1693 +, kmod
1694 +, writeShellScript
1695 +, closureInfo
1696 +, runCommand
1697 }:
1698
1699 let
1700 - apparmor-series = "2.13";
1701 - apparmor-patchver = "6";
1702 - apparmor-version = apparmor-series + "." + apparmor-patchver;
1703 + apparmor-version = "3.0.1";
1704
1705 apparmor-meta = component: with lib; {
1706 homepage = "https://apparmor.net/";
1707 description = "A mandatory access control system - ${component}";
1708 license = licenses.gpl2;
1709 - maintainers = with maintainers; [ phreedom thoughtpolice joachifm ];
1710 + maintainers = with maintainers; [ joachifm julm phreedom thoughtpolice ];
1711 platforms = platforms.linux;
1712 };
1713
1714 apparmor-sources = fetchurl {
1715 - url = "https://launchpad.net/apparmor/${apparmor-series}/${apparmor-version}/+download/apparmor-${apparmor-version}.tar.gz";
1716 - sha256 = "13xshy7905d9q9n8d8i0jmdi9m36wr525g4wlsp8k21n7yvvh9j4";
1717 + url = "https://launchpad.net/apparmor/${lib.versions.majorMinor apparmor-version}/${apparmor-version}/+download/apparmor-${apparmor-version}.tar.gz";
1718 + sha256 = "096zbg3v7b51x7f1ly61mzd3iy9alad6sd4lam98j2d6v5ragbcg";
1719 };
1720
1721 + aa-teardown = writeShellScript "aa-teardown" ''
1722 + PATH="${lib.makeBinPath [coreutils gnused gnugrep]}:$PATH"
1723 + . ${apparmor-parser}/lib/apparmor/rc.apparmor.functions
1724 + remove_profiles
1725 + '';
1726 +
1727 prePatchCommon = ''
1728 chmod a+x ./common/list_capabilities.sh ./common/list_af_names.sh
1729 patchShebangs ./common/list_capabilities.sh ./common/list_af_names.sh
1730 @@ -45,12 +56,6 @@ let
1731 name = "0003-Added-missing-typedef-definitions-on-parser.patch";
1732 sha256 = "0yyaqz8jlmn1bm37arggprqz0njb4lhjni2d9c8qfqj0kll0bam0";
1733 })
1734 - (fetchpatch {
1735 - url = "https://git.alpinelinux.org/aports/plain/testing/apparmor/0007-Do-not-build-install-vim-file-with-utils-package.patch?id=74b8427cc21f04e32030d047ae92caa618105b53";
1736 - name = "0007-Do-not-build-install-vim-file-with-utils-package.patch";
1737 - sha256 = "1m4dx901biqgnr4w4wz8a2z9r9dxyw7wv6m6mqglqwf2lxinqmp4";
1738 - })
1739 - # (alpine patches {1,4,5,6,8} are needed for apparmor 2.11, but not 2.12)
1740 ];
1741
1742 # Set to `true` after the next FIXME gets fixed or this gets some
1743 @@ -121,7 +126,11 @@ let
1744 libapparmor.python
1745 ];
1746
1747 - prePatch = prePatchCommon + ''
1748 + prePatch = prePatchCommon +
1749 + # Do not build vim file
1750 + lib.optionalString stdenv.hostPlatform.isMusl ''
1751 + sed -i ./utils/Makefile -e "/\<vim\>/d"
1752 + '' + ''
1753 substituteInPlace ./utils/apparmor/easyprof.py --replace "/sbin/apparmor_parser" "${apparmor-parser}/bin/apparmor_parser"
1754 substituteInPlace ./utils/apparmor/aa.py --replace "/sbin/apparmor_parser" "${apparmor-parser}/bin/apparmor_parser"
1755 substituteInPlace ./utils/logprof.conf --replace "/sbin/apparmor_parser" "${apparmor-parser}/bin/apparmor_parser"
1756 @@ -132,7 +141,8 @@ let
1757 installFlags = [ "DESTDIR=$(out)" "BINDIR=$(out)/bin" "VIM_INSTALL_PATH=$(out)/share" "PYPREFIX=" ];
1758
1759 postInstall = ''
1760 - for prog in aa-audit aa-autodep aa-cleanprof aa-complain aa-disable aa-enforce aa-genprof aa-logprof aa-mergeprof aa-status aa-unconfined ; do
1761 + sed -i $out/bin/aa-unconfined -e "/my_env\['PATH'\]/d"
1762 + for prog in aa-audit aa-autodep aa-cleanprof aa-complain aa-disable aa-enforce aa-genprof aa-logprof aa-mergeprof aa-unconfined ; do
1763 wrapProgram $out/bin/$prog --prefix PYTHONPATH : "$out/lib/${python.libPrefix}/site-packages:$PYTHONPATH"
1764 done
1765
1766 @@ -140,6 +150,13 @@ let
1767 # aa-notify checks its name and does not work named ".aa-notify-wrapped"
1768 mv $out/bin/aa-notify $out/bin/aa-notify-wrapped
1769 makeWrapper ${perl}/bin/perl $out/bin/aa-notify --set PERL5LIB ${libapparmor}/${perl.libPrefix} --add-flags $out/bin/aa-notify-wrapped
1770 +
1771 + substituteInPlace $out/bin/aa-remove-unknown \
1772 + --replace "/lib/apparmor/rc.apparmor.functions" "${apparmor-parser}/lib/apparmor/rc.apparmor.functions"
1773 + wrapProgram $out/bin/aa-remove-unknown \
1774 + --prefix PATH : ${lib.makeBinPath [gawk]}
1775 +
1776 + ln -s ${aa-teardown} $out/bin/aa-teardown
1777 '';
1778
1779 inherit doCheck;
1780 @@ -167,7 +184,7 @@ let
1781 prePatch = prePatchCommon;
1782 postPatch = "cd ./binutils";
1783 makeFlags = [ "LANGS=" "USE_SYSTEM=1" ];
1784 - installFlags = [ "DESTDIR=$(out)" "BINDIR=$(out)/bin" ];
1785 + installFlags = [ "DESTDIR=$(out)" "BINDIR=$(out)/bin" "SBINDIR=$(out)/bin" ];
1786
1787 inherit doCheck;
1788
1789 @@ -188,6 +205,9 @@ let
1790 substituteInPlace ./parser/Makefile --replace "/usr/include/linux/capability.h" "${linuxHeaders}/include/linux/capability.h"
1791 ## techdoc.pdf still doesn't build ...
1792 substituteInPlace ./parser/Makefile --replace "manpages htmlmanpages pdf" "manpages htmlmanpages"
1793 + substituteInPlace parser/rc.apparmor.functions \
1794 + --replace "/sbin/apparmor_parser" "$out/bin/apparmor_parser"
1795 + sed -i parser/rc.apparmor.functions -e '2i . ${./fix-rc.apparmor.functions.sh}'
1796 '';
1797 inherit patches;
1798 postPatch = "cd ./parser";
1799 @@ -249,8 +269,35 @@ let
1800 meta = apparmor-meta "kernel patches";
1801 };
1802
1803 + # Generate generic AppArmor rules in a file,
1804 + # from the closure of given rootPaths.
1805 + # To be included in an AppArmor profile like so:
1806 + # include "$(apparmorRulesFromClosure {} [pkgs.hello]}"
1807 + apparmorRulesFromClosure =
1808 + { # The store path of the derivation is given in $path
1809 + additionalRules ? []
1810 + # TODO: factorize here some other common paths
1811 + # that may emerge from use cases.
1812 + , baseRules ? [
1813 + "r $path"
1814 + "r $path/etc/**"
1815 + "r $path/share/**"
1816 + # Note that not all libraries are prefixed with "lib",
1817 + # eg. glibc-2.30/lib/ld-2.30.so
1818 + "mr $path/lib/**.so*"
1819 + # eg. glibc-2.30/lib/gconv/gconv-modules
1820 + "r $path/lib/**"
1821 + ]
1822 + , name ? ""
1823 + }: rootPaths: runCommand
1824 + ( "apparmor-closure-rules"
1825 + + lib.optionalString (name != "") "-${name}" ) {} ''
1826 + touch $out
1827 + while read -r path
1828 + do printf >>$out "%s,\n" ${lib.concatMapStringsSep " " (x: "\"${x}\"") (baseRules ++ additionalRules)}
1829 + done <${closureInfo {inherit rootPaths;}}/store-paths
1830 + '';
1831 in
1832 -
1833 {
1834 inherit
1835 libapparmor
1836 @@ -259,5 +306,6 @@ in
1837 apparmor-parser
1838 apparmor-pam
1839 apparmor-profiles
1840 - apparmor-kernel-patches;
1841 + apparmor-kernel-patches
1842 + apparmorRulesFromClosure;
1843 }
1844 diff --git a/pkgs/os-specific/linux/apparmor/fix-rc.apparmor.functions.sh b/pkgs/os-specific/linux/apparmor/fix-rc.apparmor.functions.sh
1845 new file mode 100644
1846 index 00000000000..ebc1baaa92d
1847 --- /dev/null
1848 +++ b/pkgs/os-specific/linux/apparmor/fix-rc.apparmor.functions.sh
1849 @@ -0,0 +1,32 @@
1850 +aa_action() {
1851 + STRING=$1
1852 + shift
1853 + $*
1854 + rc=$?
1855 + if [ $rc -eq 0 ] ; then
1856 + aa_log_success_msg $"$STRING "
1857 + else
1858 + aa_log_failure_msg $"$STRING "
1859 + fi
1860 + return $rc
1861 +}
1862 +
1863 +aa_log_success_msg() {
1864 + [ -n "$1" ] && echo -n $1
1865 + echo ": done."
1866 +}
1867 +
1868 +aa_log_warning_msg() {
1869 + [ -n "$1" ] && echo -n $1
1870 + echo ": Warning."
1871 +}
1872 +
1873 +aa_log_failure_msg() {
1874 + [ -n "$1" ] && echo -n $1
1875 + echo ": Failed."
1876 +}
1877 +
1878 +aa_log_skipped_msg() {
1879 + [ -n "$1" ] && echo -n $1
1880 + echo ": Skipped."
1881 +}
1882 diff --git a/pkgs/os-specific/linux/iputils/default.nix b/pkgs/os-specific/linux/iputils/default.nix
1883 index 56942d6d420..122a9ca1b7b 100644
1884 --- a/pkgs/os-specific/linux/iputils/default.nix
1885 +++ b/pkgs/os-specific/linux/iputils/default.nix
1886 @@ -1,6 +1,7 @@
1887 { lib, stdenv, fetchFromGitHub
1888 , meson, ninja, pkg-config, gettext, libxslt, docbook_xsl_ns
1889 , libcap, libidn2
1890 +, apparmorRulesFromClosure
1891 }:
1892
1893 let
1894 @@ -20,6 +21,8 @@ in stdenv.mkDerivation rec {
1895 sha256 = "08j2hfgnfh31vv9rn1ml7090j2lsvm9wdpdz13rz60rmyzrx9dq3";
1896 };
1897
1898 + outputs = ["out" "apparmor"];
1899 +
1900 mesonFlags = [
1901 "-DBUILD_RARPD=true"
1902 "-DBUILD_TRACEROUTE6=true"
1903 @@ -34,6 +37,25 @@ in stdenv.mkDerivation rec {
1904 nativeBuildInputs = [ meson ninja pkg-config gettext libxslt.bin docbook_xsl_ns ];
1905 buildInputs = [ libcap ]
1906 ++ lib.optional (!stdenv.hostPlatform.isMusl) libidn2;
1907 + postInstall = ''
1908 + install -D -m 644 /dev/stdin $apparmor/bin.ping <<EOF
1909 + include <tunables/global>
1910 + $out/bin/ping {
1911 + include <abstractions/base>
1912 + include <abstractions/consoles>
1913 + include <abstractions/nameservice>
1914 + include "${apparmorRulesFromClosure { name = "ping"; }
1915 + ([libcap] ++ lib.optional (!stdenv.hostPlatform.isMusl) libidn2)}"
1916 + include <local/bin.ping>
1917 + capability net_raw,
1918 + network inet raw,
1919 + network inet6 raw,
1920 + mr $out/bin/ping,
1921 + r $out/share/locale/**,
1922 + r @{PROC}/@{pid}/environ,
1923 + }
1924 + EOF
1925 + '';
1926
1927 meta = with lib; {
1928 description = "A set of small useful utilities for Linux networking";
1929 diff --git a/pkgs/tools/networking/inetutils/default.nix b/pkgs/tools/networking/inetutils/default.nix
1930 index 1290ec2bdb1..fe5a0d91585 100644
1931 --- a/pkgs/tools/networking/inetutils/default.nix
1932 +++ b/pkgs/tools/networking/inetutils/default.nix
1933 @@ -1,4 +1,6 @@
1934 -{ stdenv, lib, fetchurl, ncurses, perl, help2man }:
1935 +{ stdenv, lib, fetchurl, ncurses, perl, help2man
1936 +, apparmorRulesFromClosure
1937 +}:
1938
1939 stdenv.mkDerivation rec {
1940 name = "inetutils-1.9.4";
1941 @@ -8,6 +10,8 @@ stdenv.mkDerivation rec {
1942 sha256 = "05n65k4ixl85dc6rxc51b1b732gnmm8xnqi424dy9f1nz7ppb3xy";
1943 };
1944
1945 + outputs = ["out" "apparmor"];
1946 +
1947 patches = [
1948 ./whois-Update-Canadian-TLD-server.patch
1949 ./service-name.patch
1950 @@ -41,6 +45,22 @@ stdenv.mkDerivation rec {
1951
1952 installFlags = [ "SUIDMODE=" ];
1953
1954 + postInstall = ''
1955 + install -D -m 644 /dev/stdin $apparmor/bin.ping <<EOF
1956 + $out/bin/ping {
1957 + include <abstractions/base>
1958 + include <abstractions/consoles>
1959 + include <abstractions/nameservice>
1960 + include "${apparmorRulesFromClosure { name = "ping"; } [stdenv.cc.libc]}"
1961 + include <local/bin.ping>
1962 + capability net_raw,
1963 + network inet raw,
1964 + network inet6 raw,
1965 + mr $out/bin/ping,
1966 + }
1967 + EOF
1968 + '';
1969 +
1970 meta = with lib; {
1971 description = "Collection of common network programs";
1972
1973 diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
1974 index 8dfaf25fc04..3c055686e2e 100644
1975 --- a/pkgs/top-level/all-packages.nix
1976 +++ b/pkgs/top-level/all-packages.nix
1977 @@ -19105,7 +19105,7 @@ in
1978
1979 inherit (callPackages ../os-specific/linux/apparmor { python = python3; })
1980 libapparmor apparmor-utils apparmor-bin-utils apparmor-parser apparmor-pam
1981 - apparmor-profiles apparmor-kernel-patches;
1982 + apparmor-profiles apparmor-kernel-patches apparmorRulesFromClosure;
1983
1984 aseq2json = callPackage ../os-specific/linux/aseq2json {};
1985