]> Git — Sourcephile - sourcephile-nix.git/blob - nixpkgs/patches/transmission+apparmor.diff
wireguard: improve initrd setup
[sourcephile-nix.git] / nixpkgs / patches / transmission+apparmor.diff
1 diff --git a/nixos/doc/manual/release-notes/rl-2009.xml b/nixos/doc/manual/release-notes/rl-2009.xml
2 index 97b94c5756a..932e82ea8fe 100644
3 --- a/nixos/doc/manual/release-notes/rl-2009.xml
4 +++ b/nixos/doc/manual/release-notes/rl-2009.xml
5 @@ -623,6 +623,37 @@ systemd.services.nginx.serviceConfig.ReadWritePaths = [ "/var/www" ];
6 was removed, as udev gained native support to handle FIDO security tokens.
7 </para>
8 </listitem>
9 + <listitem>
10 + <para>
11 + The <literal>services.transmission</literal> module
12 + was enhanced with the new options:
13 + <xref linkend="opt-services.transmission.credentialsFile"/>,
14 + <xref linkend="opt-services.transmission.openFirewall"/>,
15 + and <xref linkend="opt-services.transmission.performanceNetParameters"/>.
16 + </para>
17 + <para>
18 + <literal>transmission-daemon</literal> is now started with additional systemd sandbox/hardening options for better security.
19 + Please <link xlink:href="https://github.com/NixOS/nixpkgs/issues">report</link>
20 + any use case where this is not working well.
21 + In particular, the <literal>RootDirectory</literal> option newly set
22 + forbids uploading or downloading a torrent outside of the default directory
23 + configured at <link linkend="opt-services.transmission.settings">settings.download-dir</link>.
24 + If you really need Transmission to access other directories,
25 + you must include those directories into the <literal>BindPaths</literal> of the service:
26 +<programlisting>
27 +systemd.services.transmission.serviceConfig.BindPaths = [ "/path/to/alternative/download-dir" ];
28 +</programlisting>
29 + </para>
30 + <para>
31 + Also, connection to the RPC (Remote Procedure Call) of <literal>transmission-daemon</literal>
32 + is now only available on the local network interface by default.
33 + Use:
34 +<programlisting>
35 +services.transmission.settings.rpc-bind-address = "0.0.0.0";
36 +</programlisting>
37 + to get the previous behavior of listening on all network interfaces.
38 + </para>
39 + </listitem>
40 <listitem>
41 <para>
42 With this release <literal>systemd-networkd</literal> (when enabled through <xref linkend="opt-networking.useNetworkd"/>)
43 diff --git a/nixos/modules/config/fonts/fontconfig.nix b/nixos/modules/config/fonts/fontconfig.nix
44 index ac2a024eaa8..0519812df88 100644
45 --- a/nixos/modules/config/fonts/fontconfig.nix
46 +++ b/nixos/modules/config/fonts/fontconfig.nix
47 @@ -489,6 +489,38 @@ in
48 (mkIf cfg.enable {
49 environment.systemPackages = [ pkgs.fontconfig ];
50 environment.etc.fonts.source = "${fontconfigEtc}/etc/fonts/";
51 + security.apparmor.includes."abstractions/fonts" = ''
52 + # fonts.conf
53 + r ${supportFontsConf},
54 + r ${latestPkg.out}/etc/fonts/fonts.conf,
55 +
56 + # fontconfig default config files
57 + r ${supportPkg.out}/etc/fonts/conf.d/*.conf,
58 + r ${latestPkg.out}/etc/fonts/conf.d/*.conf,
59 +
60 + # 00-nixos-cache.conf
61 + r ${cacheConfSupport},
62 + r ${cacheConfLatest},
63 +
64 + # 10-nixos-rendering.conf
65 + r ${renderConf},
66 +
67 + # local.conf (indirect priority 51)
68 + ${optionalString (cfg.localConf != "") ''
69 + r ${localConf},
70 + ''}
71 +
72 + # 52-nixos-default-fonts.conf
73 + r ${defaultFontsConf},
74 +
75 + # 53-no-bitmaps.conf
76 + r ${rejectBitmaps},
77 +
78 + ${optionalString (!cfg.allowType1) ''
79 + # 53-nixos-reject-type1.conf
80 + r ${rejectType1},
81 + ''}
82 + '';
83 })
84 (mkIf (cfg.enable && !cfg.penultimate.enable) {
85 fonts.fontconfig.confPackages = [ confPkg ];
86 diff --git a/nixos/modules/security/apparmor-suid.nix b/nixos/modules/security/apparmor-suid.nix
87 index 6c479e070e2..e6d9467f296 100644
88 --- a/nixos/modules/security/apparmor-suid.nix
89 +++ b/nixos/modules/security/apparmor-suid.nix
90 @@ -21,9 +21,9 @@ with lib;
91 };
92
93 config = mkIf (cfg.confineSUIDApplications) {
94 - security.apparmor.profiles = [ (pkgs.writeText "ping" ''
95 + security.apparmor.policies."bin/ping".profile = ''
96 #include <tunables/global>
97 - /run/wrappers/bin/ping {
98 + /run/wrappers/wrappers.*/ping {
99 #include <abstractions/base>
100 #include <abstractions/consoles>
101 #include <abstractions/nameservice>
102 @@ -32,10 +32,19 @@ with lib;
103 capability setuid,
104 network inet raw,
105
106 - ${pkgs.stdenv.cc.libc.out}/lib/*.so mr,
107 - ${pkgs.libcap.lib}/lib/libcap.so* mr,
108 - ${pkgs.attr.out}/lib/libattr.so* mr,
109 + ${getLib pkgs.stdenv.cc.cc}/lib/*.so* mr,
110 + ${getLib pkgs.stdenv.cc.libc}/lib/*.so* mr,
111 + ${getLib pkgs.stdenv.cc.libc}/lib/gconv/gconv-modules r,
112 + ${getLib pkgs.glibcLocales}/lib/locale/locale-archive r,
113 + ${getLib pkgs.attr.out}/lib/libattr.so* mr,
114 + ${getLib pkgs.libcap.lib}/lib/libcap.so* mr,
115 + ${getLib pkgs.libcap_ng}/lib/libcap-ng.so* mr,
116 + ${getLib pkgs.libidn2}/lib/libidn2.so* mr,
117 + ${getLib pkgs.libunistring}/lib/libunistring.so* mr,
118 + ${getLib pkgs.nettle}/lib/libnettle.so* mr,
119
120 + #@{PROC}/@{pid}/environ r,
121 + /run/wrappers/wrappers.*/ping.real r,
122 ${pkgs.iputils}/bin/ping mixr,
123
124 #/etc/modules.conf r,
125 @@ -43,7 +52,7 @@ with lib;
126 ## Site-specific additions and overrides. See local/README for details.
127 ##include <local/bin.ping>
128 }
129 - '') ];
130 + '';
131 };
132
133 }
134 diff --git a/nixos/modules/security/apparmor.nix b/nixos/modules/security/apparmor.nix
135 index cfc65b347bc..f9abb18afd2 100644
136 --- a/nixos/modules/security/apparmor.nix
137 +++ b/nixos/modules/security/apparmor.nix
138 @@ -1,59 +1,190 @@
139 { config, lib, pkgs, ... }:
140
141 let
142 - inherit (lib) mkIf mkOption types concatMapStrings;
143 + inherit (builtins) head match readFile;
144 + inherit (lib) types;
145 + inherit (config.environment) etc;
146 cfg = config.security.apparmor;
147 + mkDisableOption = name: lib.mkEnableOption name // {
148 + default = true;
149 + example = false;
150 + };
151 in
152
153 {
154 - options = {
155 - security.apparmor = {
156 - enable = mkOption {
157 - type = types.bool;
158 - default = false;
159 - description = "Enable the AppArmor Mandatory Access Control system.";
160 - };
161 - profiles = mkOption {
162 - type = types.listOf types.path;
163 - default = [];
164 - description = "List of files containing AppArmor profiles.";
165 - };
166 - packages = mkOption {
167 - type = types.listOf types.package;
168 - default = [];
169 - description = "List of packages to be added to apparmor's include path";
170 - };
171 - };
172 - };
173 + imports = [
174 + (lib.mkRemovedOptionModule [ "security" "apparmor" "profiles" ] "Please use the new option: `security.apparmor.policies'.")
175 + apparmor/profiles.nix
176 + ];
177 + options = {
178 + security.apparmor = {
179 + enable = lib.mkEnableOption "the AppArmor Mandatory Access Control system";
180 + policies = lib.mkOption {
181 + description = ''
182 + AppArmor policies.
183 + '';
184 + type = types.attrsOf (types.submodule ({ name, config, ... }: {
185 + options = {
186 + enable = mkDisableOption "loading of the profile into the kernel";
187 + enforce = mkDisableOption "enforcing of the policy or only complain in the logs";
188 + profile = lib.mkOption {
189 + description = "The policy of the profile.";
190 + type = types.lines;
191 + apply = pkgs.writeText name;
192 + };
193 + };
194 + }));
195 + default = {};
196 + };
197 + includes = lib.mkOption {
198 + type = types.attrsOf types.lines;
199 + default = [];
200 + description = ''
201 + List of paths to be added to AppArmor's searched paths
202 + when resolving absolute #include directives.
203 + '';
204 + apply = lib.mapAttrs pkgs.writeText;
205 + };
206 + packages = lib.mkOption {
207 + type = types.listOf types.package;
208 + default = [];
209 + description = "List of packages to be added to AppArmor's include path";
210 + };
211 + enableCache = lib.mkEnableOption ''caching of AppArmor policies
212 + in <literal>/var/cache/apparmor/</literal>.
213 + Beware that AppArmor policies almost always contain Nix store paths,
214 + and thus produce at each change of these paths
215 + a new cached version accumulating in the cache.
216 + '';
217 + killUnconfinedConfinables = mkDisableOption ''killing of processes
218 + which have an AppArmor profile enabled
219 + (in <link linkend="opt-security.apparmor.policies">policies</link>)
220 + but are not confined (because AppArmor can only confine new processes).
221 + '';
222 + };
223 + };
224
225 - config = mkIf cfg.enable {
226 - environment.systemPackages = [ pkgs.apparmor-utils ];
227 + config = lib.mkIf cfg.enable {
228 + environment.systemPackages = [ pkgs.apparmor-utils ];
229 + environment.etc."apparmor.d".source = pkgs.linkFarm "apparmor.d" (
230 + lib.mapAttrsToList (name: p: {inherit name; path=p.profile;}) cfg.policies ++
231 + lib.mapAttrsToList (name: path: {inherit name path;}) cfg.includes
232 + );
233 + environment.etc."apparmor/parser.conf".text = ''
234 + ${if cfg.enableCache then "write-cache" else "skip-cache"}
235 + cache-loc /var/cache/apparmor
236 + Include /etc/apparmor.d
237 + '' +
238 + lib.concatMapStrings (p: "Include ${p}/etc/apparmor.d\n") cfg.packages;
239 + environment.etc."apparmor/logprof.conf".text = ''
240 + [settings]
241 + profiledir = /etc/apparmor.d
242 + inactive_profiledir = ${pkgs.apparmor-profiles}/share/apparmor/extra-profiles
243 + logfiles = /var/log/audit/audit.log /var/log/syslog /var/log/messages
244
245 - boot.kernelParams = [ "apparmor=1" "security=apparmor" ];
246 + parser = ${pkgs.apparmor-parser}/bin/apparmor_parser
247 + ldd = ${pkgs.glibc.bin}/bin/ldd
248 + logger = ${pkgs.utillinux}/bin/logger
249
250 - systemd.services.apparmor = let
251 - paths = concatMapStrings (s: " -I ${s}/etc/apparmor.d")
252 - ([ pkgs.apparmor-profiles ] ++ cfg.packages);
253 - in {
254 - after = [ "local-fs.target" ];
255 - before = [ "sysinit.target" ];
256 - wantedBy = [ "multi-user.target" ];
257 - unitConfig = {
258 - DefaultDependencies = "no";
259 - };
260 - serviceConfig = {
261 - Type = "oneshot";
262 - RemainAfterExit = "yes";
263 - ExecStart = map (p:
264 - ''${pkgs.apparmor-parser}/bin/apparmor_parser -rKv ${paths} "${p}"''
265 - ) cfg.profiles;
266 - ExecStop = map (p:
267 - ''${pkgs.apparmor-parser}/bin/apparmor_parser -Rv "${p}"''
268 - ) cfg.profiles;
269 - ExecReload = map (p:
270 - ''${pkgs.apparmor-parser}/bin/apparmor_parser --reload ${paths} "${p}"''
271 - ) cfg.profiles;
272 - };
273 - };
274 - };
275 + # customize how file ownership permissions are presented
276 + # 0 - off
277 + # 1 - default of what ever mode the log reported
278 + # 2 - force the new permissions to be user
279 + # 3 - force all perms on the rule to be user
280 + default_owner_prompt = 1
281 +
282 + # custom directory locations to look for #includes
283 + #
284 + # each name should be a valid directory containing possible #include
285 + # candidate files under the profile dir which by default is /etc/apparmor.d.
286 + #
287 + # So an entry of my-includes will allow /etc/apparmor.d/my-includes to
288 + # be used by the yast UI and profiling tools as a source of #include
289 + # files.
290 + custom_includes =
291 +
292 + [qualifiers]
293 + ${pkgs.runtimeShell} = icnu
294 + ${pkgs.bashInteractive}/bin/sh = icnu
295 + ${pkgs.bashInteractive}/bin/bash = icnu
296 + '' + head (match "^.*\\[qualifiers](.*)" # Drop the original [settings] section.
297 + (readFile "${pkgs.apparmor-utils}/etc/apparmor/logprof.conf"));
298 +
299 + boot.kernelParams = [ "apparmor=1" "security=apparmor" ];
300 +
301 + systemd.services.apparmor = {
302 + after = [
303 + "local-fs.target"
304 + "systemd-journald-audit.socket"
305 + ];
306 + before = [ "sysinit.target" ];
307 + wantedBy = [ "multi-user.target" ];
308 + restartTriggers = [
309 + etc."apparmor/parser.conf".source
310 + etc."apparmor.d".source
311 + ];
312 + unitConfig = {
313 + Description="Load AppArmor policies";
314 + DefaultDependencies = "no";
315 + ConditionSecurity = "apparmor";
316 + };
317 + # Reloading instead of restarting enables to load new AppArmor profiles
318 + # without necessarily restarting all services which have Requires=apparmor.service
319 + # It works by:
320 + # - Adding or replacing into the kernel profiles enabled in cfg.policies
321 + # (because AppArmor can do that without stopping the processes already confined).
322 + # - Removing from the kernel any profile whose name is not
323 + # one of the names within the content of the profiles in cfg.policies.
324 + # - Killing the processes which are unconfined but now have a profile loaded
325 + # (because AppArmor can only confine new processes).
326 + reloadIfChanged = true;
327 + # Avoid searchs in /usr/share/locale/
328 + environment.LANG="C";
329 + serviceConfig = let
330 + enabledPolicies = lib.attrValues (lib.filterAttrs (n: p: p.enable) cfg.policies);
331 + removeDisabledProfiles = pkgs.writeShellScript "apparmor-remove" ''
332 + set -eux
333 +
334 + enabledProfiles=$(mktemp)
335 + loadedProfiles=$(mktemp)
336 + trap "rm -f $enabledProfiles $loadedProfiles" EXIT
337 +
338 + ${pkgs.apparmor-parser}/bin/apparmor_parser --names /dev/null ${
339 + lib.concatMapStrings (p: "\\\n "+p.profile) enabledPolicies} |
340 + sort -u >"$enabledProfiles"
341 +
342 + sed -e "s/ (\(enforce\|complain\))$//" /sys/kernel/security/apparmor/profiles |
343 + sort -u >"$loadedProfiles"
344 +
345 + comm -23 "$loadedProfiles" "$enabledProfiles" |
346 + while IFS=$'\n\r' read -r profile
347 + do printf %s "$profile" >/sys/kernel/security/apparmor/.remove
348 + done
349 + '';
350 + killUnconfinedConfinables = pkgs.writeShellScript "apparmor-kill" ''
351 + set -eux
352 + ${pkgs.apparmor-utils}/bin/aa-status --json |
353 + ${pkgs.jq}/bin/jq --raw-output '.processes | .[] | .[] | select (.status == "unconfined") | .pid' |
354 + xargs --verbose --no-run-if-empty --delimiter='\n' \
355 + kill
356 + '';
357 + commonOpts = p: "--verbose --show-cache ${lib.optionalString (!p.enforce) "--complain "}${p.profile}";
358 + in {
359 + Type = "oneshot";
360 + RemainAfterExit = "yes";
361 + ExecStartPre = "${pkgs.apparmor-utils}/bin/aa-teardown";
362 + ExecStart = map (p: "${pkgs.apparmor-parser}/bin/apparmor_parser --add ${commonOpts p}") enabledPolicies;
363 + ExecStartPost = lib.optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
364 + ExecReload =
365 + map (p: "${pkgs.apparmor-parser}/bin/apparmor_parser --replace ${commonOpts p}") enabledPolicies ++
366 + [ removeDisabledProfiles ] ++
367 + lib.optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
368 + ExecStop = "${pkgs.apparmor-utils}/bin/aa-teardown";
369 + CacheDirectory = [ "apparmor" ];
370 + CacheDirectoryMode = "0700";
371 + };
372 + };
373 + };
374 +
375 + meta.maintainers = with lib.maintainers; [ julm ];
376 }
377 diff --git a/nixos/modules/security/apparmor/profiles.nix b/nixos/modules/security/apparmor/profiles.nix
378 new file mode 100644
379 index 00000000000..7e33e630798
380 --- /dev/null
381 +++ b/nixos/modules/security/apparmor/profiles.nix
382 @@ -0,0 +1,333 @@
383 +{ config, lib, pkgs, ... }:
384 +let
385 + inherit (builtins) attrNames hasAttr isAttrs;
386 + inherit (lib) getLib;
387 + inherit (config.environment) etc;
388 + etcRule = arg:
389 + let go = {path ? null, mode ? "r", trail ? ""}:
390 + lib.optionalString (hasAttr path etc)
391 + "${mode} ${config.environment.etc."${path}".source}${trail},";
392 + in if isAttrs arg
393 + then go arg
394 + else go {path=arg;};
395 +in
396 +{
397 +config.security.apparmor.packages = [ pkgs.apparmor-profiles ];
398 +# FIXME: most of the etcRule calls below have been
399 +# written systematically by converting from apparmor-profiles's profiles
400 +# without testing nor deep understanding of their uses,
401 +# and thus may need more rules or can have less rules;
402 +# this remains to me determined case by case,
403 +# some may even be completely useless.
404 +config.security.apparmor.includes = {
405 + # This one is included by <tunables/global>
406 + # which is usualy included before any profile.
407 + "abstractions/tunables/alias" = ''
408 + alias /bin -> /run/current-system/sw/bin,
409 + # Unfortunately /etc is mainly built using symlinks,
410 + # thus aliasing does not work.
411 + #alias /etc -> /run/current-system/etc,
412 + alias /lib/modules -> /run/current-system/kernel/lib/modules,
413 + alias /sbin -> /run/current-system/sw/sbin,
414 + alias /usr -> /run/current-system/sw,
415 + '';
416 + "abstractions/audio" = ''
417 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/audio
418 + ${etcRule "asound.conf"}
419 + ${etcRule "esound/esd.conf"}
420 + ${etcRule "libao.conf"}
421 + ${etcRule {path="pulse"; trail="/";}}
422 + ${etcRule {path="pulse"; trail="/**";}}
423 + ${etcRule {path="sound"; trail="/";}}
424 + ${etcRule {path="sound"; trail="/**";}}
425 + ${etcRule {path="alsa/conf.d"; trail="/";}}
426 + ${etcRule {path="alsa/conf.d"; trail="/*";}}
427 + ${etcRule "openal/alsoft.conf"}
428 + ${etcRule "wildmidi/wildmidi.conf"}
429 + '';
430 + # FIXME: security.pam configures more .so than allowed here,
431 + # but has many tests to decide what .so to use,
432 + # so it would be simpler to let security.pam add those .so
433 + # to the present security.apparmor.includes."abstractions/authentication"
434 + "abstractions/authentication" = ''
435 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/authentication
436 + ${etcRule "nologin"}
437 + ${lib.concatMapStringsSep "\n"
438 + (name: "r ${etc."pam.d/${name}".source},")
439 + (attrNames config.security.pam.services)}
440 + mr ${getLib pkgs.pam}/lib/security/pam_filter/*,
441 + mr ${getLib pkgs.pam}/lib/security/pam_*.so,
442 + r ${getLib pkgs.pam}/lib/security/,
443 + ${etcRule "securetty"}
444 + ${etcRule {path="security"; trail="/*";}}
445 + ${etcRule "shadow"}
446 + ${etcRule "gshadow"}
447 + ${etcRule "pwdb.conf"}
448 + ${etcRule "default/passwd"}
449 + ${etcRule "login.defs"}
450 + '';
451 + "abstractions/base" = ''
452 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/base
453 + r ${pkgs.stdenv.cc.libc}/share/locale/**,
454 + r ${pkgs.stdenv.cc.libc}/share/locale.alias,
455 + ${etcRule "localtime"}
456 + r /etc/ld-nix.so.preload,
457 + ${etcRule "ld-nix.so.preload"}
458 + ${lib.concatMapStrings (p: lib.optionalString (p != "") "mr ${p},\n")
459 + (lib.splitString "\n" etc."ld-nix.so.preload".text)
460 + # TODO: avoid this line splitting by nixifying ld-nix.so.preload as a list or attrset,
461 + # and make services.config.malloc use it.
462 + }
463 + r ${pkgs.tzdata}/share/zoneinfo/**,
464 + r ${pkgs.stdenv.cc.libc}/share/i18n/**,
465 + '';
466 + "abstractions/bash" = ''
467 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/bash
468 + # system-wide bash configuration
469 + ${etcRule "profile.dos"}
470 + ${etcRule "profile"}
471 + ${etcRule "profile.d"}
472 + ${etcRule {path="profile.d"; trail="/*";}}
473 + ${etcRule "bashrc"}
474 + ${etcRule "bash.bashrc"}
475 + ${etcRule "bash.bashrc.local"}
476 + ${etcRule "bash_completion"}
477 + ${etcRule "bash_completion.d"}
478 + ${etcRule {path="bash_completion.d"; trail="/*";}}
479 + # bash relies on system-wide readline configuration
480 + ${etcRule "inputrc"}
481 + # bash inspects filesystems at startup
482 + # and /etc/mtab is linked to /proc/mounts
483 + @{PROC}/mounts
484 +
485 + # run out of /etc/bash.bashrc
486 + ${etcRule "DIR_COLORS"}
487 + '';
488 + "abstractions/cups-client" = ''
489 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/cpus-client
490 + ${etcRule "cups/cups-client.conf"}
491 + '';
492 + "abstractions/consoles" = ''
493 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/consoles
494 + '';
495 + "abstractions/dbus-session-strict" = ''
496 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/dbus-session-strict
497 + ${etcRule "machine-id"}
498 + '';
499 + "abstractions/dconf" = ''
500 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/dconf
501 + ${etcRule {path="dconf"; trail="/**";}}
502 + '';
503 + "abstractions/dri-common" = ''
504 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/dri-common
505 + ${etcRule "drirc"}
506 + '';
507 + # The config.fonts.fontconfig NixOS module adds many files to /etc/fonts/
508 + # by symlinking them but without exporting them outside of its NixOS module,
509 + # those are therefore added there to this "abstractions/fonts".
510 + "abstractions/fonts" = ''
511 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/fonts
512 + ${etcRule {path="fonts"; trail="/**";}}
513 + '';
514 + "abstractions/gnome" = ''
515 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/gnome
516 + ${etcRule {path="gnome"; trail="/gtkrc*";}}
517 + ${etcRule {path="gtk"; trail="/*";}}
518 + ${etcRule {path="gtk-2.0"; trail="/*";}}
519 + ${etcRule {path="gtk-3.0"; trail="/*";}}
520 + ${etcRule "orbitrc"}
521 + #include <abstractions/fonts>
522 + ${etcRule {path="pango"; trail="/*";}}
523 + ${etcRule {path="/etc/gnome-vfs-2.0"; trail="/modules/";}}
524 + ${etcRule {path="/etc/gnome-vfs-2.0"; trail="/modules/*";}}
525 + ${etcRule "papersize"}
526 + ${etcRule {path="cups"; trail="/lpoptions";}}
527 + ${etcRule {path="gnome"; trail="/defaults.list";}}
528 + ${etcRule {path="xdg"; trail="/{,*-}mimeapps.list";}}
529 + ${etcRule "xdg/mimeapps.list"}
530 + '';
531 + "abstractions/kde" = ''
532 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/kde
533 + ${etcRule {path="qt3"; trail="/kstylerc";}}
534 + ${etcRule {path="qt3"; trail="/qt_plugins_3.3rc";}}
535 + ${etcRule {path="qt3"; trail="/qtrc";}}
536 + ${etcRule "kderc"}
537 + ${etcRule {path="kde3"; trail="/*";}}
538 + ${etcRule "kde4rc"}
539 + ${etcRule {path="xdg"; trail="/kdeglobals";}}
540 + ${etcRule {path="xdg"; trail="/Trolltech.conf";}}
541 + '';
542 + "abstractions/kerberosclient" = ''
543 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/kerberosclient
544 + ${etcRule {path="krb5.keytab"; mode="rk";}}
545 + ${etcRule "krb5.conf"}
546 + ${etcRule "krb5.conf.d"}
547 + ${etcRule {path="krb5.conf.d"; trail="/*";}}
548 +
549 + # config files found via strings on libs
550 + ${etcRule "krb.conf"}
551 + ${etcRule "krb.realms"}
552 + ${etcRule "srvtab"}
553 + '';
554 + "abstractions/ldapclient" = ''
555 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/ldapclient
556 + ${etcRule "ldap.conf"}
557 + ${etcRule "ldap.secret"}
558 + ${etcRule {path="openldap"; trail="/*";}}
559 + ${etcRule {path="openldap"; trail="/cacerts/*";}}
560 + ${etcRule {path="sasl2"; trail="/*";}}
561 + '';
562 + "abstractions/likewise" = ''
563 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/likewise
564 + '';
565 + "abstractions/mdns" = ''
566 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/mdns
567 + ${etcRule "nss_mdns.conf"}
568 + '';
569 + "abstractions/nameservice" = ''
570 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nameservice
571 +
572 + # Many programs wish to perform nameservice-like operations, such as
573 + # looking up users by name or id, groups by name or id, hosts by name
574 + # or IP, etc. These operations may be performed through files, dns,
575 + # NIS, NIS+, LDAP, hesiod, wins, etc. Allow them all here.
576 + ${etcRule "group"}
577 + ${etcRule "host.conf"}
578 + ${etcRule "hosts"}
579 + ${etcRule "nsswitch.conf"}
580 + ${etcRule "gai.conf"}
581 + ${etcRule "passwd"}
582 + ${etcRule "protocols"}
583 +
584 + # libtirpc (used for NIS/YP login) needs this
585 + ${etcRule "netconfig"}
586 +
587 + ${etcRule "resolv.conf"}
588 +
589 + ${etcRule {path="samba"; trail="/lmhosts";}}
590 + ${etcRule "services"}
591 +
592 + ${etcRule "default/nss"}
593 +
594 + # libnl-3-200 via libnss-gw-name
595 + ${etcRule {path="libnl"; trail="/classid";}}
596 + ${etcRule {path="libnl-3"; trail="/classid";}}
597 +
598 + mr ${getLib pkgs.nss}/lib/libnss_*.so*,
599 + mr ${getLib pkgs.nss}/lib64/libnss_*.so*,
600 + '';
601 + "abstractions/nis" = ''
602 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nis
603 + '';
604 + "abstractions/nvidia" = ''
605 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nvidia
606 + ${etcRule "vdpau_wrapper.cfg"}
607 + '';
608 + "abstractions/opencl-common" = ''
609 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/opencl-common
610 + ${etcRule {path="OpenCL"; trail="/**";}}
611 + '';
612 + "abstractions/opencl-mesa" = ''
613 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/opencl-mesa
614 + ${etcRule "default/drirc"}
615 + '';
616 + "abstractions/openssl" = ''
617 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/openssl
618 + ${etcRule {path="ssl"; trail="/openssl.cnf";}}
619 + '';
620 + "abstractions/p11-kit" = ''
621 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/p11-kit
622 + ${etcRule {path="pkcs11"; trail="/";}}
623 + ${etcRule {path="pkcs11"; trail="/pkcs11.conf";}}
624 + ${etcRule {path="pkcs11"; trail="/modules/";}}
625 + ${etcRule {path="pkcs11"; trail="/modules/*";}}
626 + '';
627 + "abstractions/perl" = ''
628 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/perl
629 + ${etcRule {path="perl"; trail="/**";}}
630 + '';
631 + "abstractions/php" = ''
632 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/php
633 + ${etcRule {path="php"; trail="/**/";}}
634 + ${etcRule {path="php5"; trail="/**/";}}
635 + ${etcRule {path="php7"; trail="/**/";}}
636 + ${etcRule {path="php"; trail="/**.ini";}}
637 + ${etcRule {path="php5"; trail="/**.ini";}}
638 + ${etcRule {path="php7"; trail="/**.ini";}}
639 + '';
640 + "abstractions/postfix-common" = ''
641 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/postfix-common
642 + ${etcRule "mailname"}
643 + ${etcRule {path="postfix"; trail="/*.cf";}}
644 + ${etcRule "postfix/main.cf"}
645 + ${etcRule "postfix/master.cf"}
646 + '';
647 + "abstractions/python" = ''
648 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/python
649 + ${etcRule {path="python2.4"; trail="/**";}}
650 + ${etcRule {path="python2.5"; trail="/**";}}
651 + ${etcRule {path="python2.6"; trail="/**";}}
652 + ${etcRule {path="python2.7"; trail="/**";}}
653 + ${etcRule {path="python3.0"; trail="/**";}}
654 + ${etcRule {path="python3.1"; trail="/**";}}
655 + ${etcRule {path="python3.2"; trail="/**";}}
656 + ${etcRule {path="python3.3"; trail="/**";}}
657 + ${etcRule {path="python3.4"; trail="/**";}}
658 + ${etcRule {path="python3.5"; trail="/**";}}
659 + ${etcRule {path="python3.6"; trail="/**";}}
660 + ${etcRule {path="python3.7"; trail="/**";}}
661 + ${etcRule {path="python3.8"; trail="/**";}}
662 + ${etcRule {path="python3.9"; trail="/**";}}
663 + '';
664 + "abstractions/qt5" = ''
665 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/qt5
666 + ${etcRule {path="xdg"; trail="/QtProject/qtlogging.ini";}}
667 + ${etcRule {path="xdg/QtProject"; trail="/qtlogging.ini";}}
668 + ${etcRule "xdg/QtProject/qtlogging.ini"}
669 + '';
670 + "abstractions/samba" = ''
671 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/samba
672 + ${etcRule {path="samba"; trail="/*";}}
673 + '';
674 + "abstractions/ssl_certs" = ''
675 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/ssl_certs
676 + ${etcRule "ssl/certs/ca-certificates.crt"}
677 + ${etcRule "ssl/certs/ca-bundle.crt"}
678 + ${etcRule "pki/tls/certs/ca-bundle.crt"}
679 +
680 + ${etcRule {path="ssl/trust"; trail="/";}}
681 + ${etcRule {path="ssl/trust"; trail="/*";}}
682 + ${etcRule {path="ssl/trust/anchors"; trail="/";}}
683 + ${etcRule {path="ssl/trust/anchors"; trail="/**";}}
684 + ${etcRule {path="pki/trust"; trail="/";}}
685 + ${etcRule {path="pki/trust"; trail="/*";}}
686 + ${etcRule {path="pki/trust/anchors"; trail="/";}}
687 + ${etcRule {path="pki/trust/anchors"; trail="/**";}}
688 +
689 + # security.acme NixOS module
690 + r /var/lib/acme/*/cert.pem,
691 + r /var/lib/acme/*/chain.pem,
692 + r /var/lib/acme/*/fullchain.pem,
693 + '';
694 + "abstractions/ssl_keys" = ''
695 + # security.acme NixOS module
696 + r /var/lib/acme/*/full.pem,
697 + r /var/lib/acme/*/key.pem,
698 + '';
699 + "abstractions/vulkan" = ''
700 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/vulkan
701 + ${etcRule {path="vulkan/icd.d"; trail="/";}}
702 + ${etcRule {path="vulkan/icd.d"; trail="/*.json";}}
703 + '';
704 + "abstractions/winbind" = ''
705 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/winbind
706 + ${etcRule {path="samba"; trail="/smb.conf";}}
707 + ${etcRule {path="samba"; trail="/dhcp.conf";}}
708 + '';
709 + "abstractions/X" = ''
710 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/X
711 + ${etcRule {path="X11/cursors"; trail="/";}}
712 + ${etcRule {path="X11/cursors"; trail="/**";}}
713 + '';
714 +};
715 +}
716 diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
717 index 688344852ae..339dbd4cb3a 100644
718 --- a/nixos/modules/security/pam.nix
719 +++ b/nixos/modules/security/pam.nix
720 @@ -789,6 +789,57 @@ in
721 runuser-l = { rootOK = true; unixAuth = false; };
722 };
723
724 + security.apparmor.includes."abstractions/pam" = let
725 + isEnabled = test: fold or false (map test (attrValues config.security.pam.services));
726 + in ''
727 + ${optionalString use_ldap
728 + "mr ${pam_ldap}/lib/security/pam_ldap.so,"}
729 + ${optionalString config.services.sssd.enable
730 + "mr ${pkgs.sssd}/lib/security/pam_sss.so,"}
731 + ${optionalString config.krb5.enable ''
732 + mr ${pam_krb5}/lib/security/pam_krb5.so,
733 + mr ${pam_ccreds}/lib/security/pam_ccreds.so,
734 + ''}
735 + ${optionalString (isEnabled (cfg: cfg.googleOsLoginAccountVerification)) ''
736 + mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so,
737 + mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_admin.so,
738 + ''}
739 + ${optionalString (isEnabled (cfg: cfg.googleOsLoginAuthentication))
740 + "mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so,"}
741 + ${optionalString (config.security.pam.enableSSHAgentAuth && isEnabled (cfg: cfg.sshAgentAuth))
742 + "mr ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so,"}
743 + ${optionalString (isEnabled (cfg: cfg.fprintAuth))
744 + "mr ${pkgs.fprintd}/lib/security/pam_fprintd.so,"}
745 + ${optionalString (isEnabled (cfg: cfg.u2fAuth))
746 + "mr ${pkgs.pam_u2f}/lib/security/pam_u2f.so,"}
747 + ${optionalString (isEnabled (cfg: cfg.usbAuth))
748 + "mr ${pkgs.pam_usb}/lib/security/pam_usb.so,"}
749 + ${optionalString (isEnabled (cfg: cfg.oathAuth))
750 + "mr ${pkgs.oathToolkit}/lib/security/pam_oath.so,"}
751 + ${optionalString (isEnabled (cfg: cfg.yubicoAuth))
752 + "mr ${pkgs.yubico-pam}/lib/security/pam_yubico.so,"}
753 + ${optionalString (isEnabled (cfg: cfg.duoSecurity.enable))
754 + "mr ${pkgs.duo-unix}/lib/security/pam_duo.so,"}
755 + ${optionalString (isEnabled (cfg: cfg.otpwAuth))
756 + "mr ${pkgs.otpw}/lib/security/pam_otpw.so,"}
757 + ${optionalString config.security.pam.enableEcryptfs
758 + "mr ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so,"}
759 + ${optionalString (isEnabled (cfg: cfg.pamMount))
760 + "mr ${pkgs.pam_mount}/lib/security/pam_mount.so,"}
761 + ${optionalString config.services.samba.syncPasswordsByPam
762 + "mr ${pkgs.samba}/lib/security/pam_smbpass.so,"}
763 + ${optionalString (isEnabled (cfg: cfg.enableGnomeKeyring))
764 + "mr ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so,"}
765 + ${optionalString (isEnabled (cfg: cfg.startSession))
766 + "mr ${pkgs.systemd}/lib/security/pam_systemd.so,"}
767 + ${optionalString (isEnabled (cfg: cfg.enableAppArmor) && config.security.apparmor.enable)
768 + "mr ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so,"}
769 + ${optionalString (isEnabled (cfg: cfg.enableKwallet))
770 + "mr ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so,"}
771 + ${optionalString config.virtualisation.lxc.lxcfs.enable
772 + "mr ${pkgs.lxc}/lib/security/pam_cgfs.so"}
773 + '';
774 +
775 };
776
777 }
778 diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
779 index 1bfcf2de82f..6fe54000c0d 100644
780 --- a/nixos/modules/services/torrent/transmission.nix
781 +++ b/nixos/modules/services/torrent/transmission.nix
782 @@ -1,52 +1,50 @@
783 -{ config, lib, pkgs, ... }:
784 +{ config, lib, pkgs, options, ... }:
785
786 with lib;
787
788 let
789 cfg = config.services.transmission;
790 + inherit (config.environment) etc;
791 apparmor = config.security.apparmor.enable;
792 -
793 - homeDir = cfg.home;
794 - downloadDirPermissions = cfg.downloadDirPermissions;
795 - downloadDir = "${homeDir}/Downloads";
796 - incompleteDir = "${homeDir}/.incomplete";
797 -
798 - settingsDir = "${homeDir}/config";
799 - settingsFile = pkgs.writeText "settings.json" (builtins.toJSON fullSettings);
800 -
801 - # for users in group "transmission" to have access to torrents
802 - fullSettings = { umask = 2; download-dir = downloadDir; incomplete-dir = incompleteDir; } // cfg.settings;
803 -
804 - preStart = pkgs.writeScript "transmission-pre-start" ''
805 - #!${pkgs.runtimeShell}
806 - set -ex
807 - cp -f ${settingsFile} ${settingsDir}/settings.json
808 - '';
809 + rootDir = "/run/transmission";
810 + homeDir = "/var/lib/transmission";
811 + settingsDir = ".config/transmission-daemon";
812 + downloadsDir = "Downloads";
813 + incompleteDir = ".incomplete";
814 + # TODO: switch to configGen.json once RFC0042 is implemented
815 + settingsFile = pkgs.writeText "settings.json" (builtins.toJSON cfg.settings);
816 in
817 {
818 options = {
819 services.transmission = {
820 - enable = mkOption {
821 - type = types.bool;
822 - default = false;
823 - description = ''
824 - Whether or not to enable the headless Transmission BitTorrent daemon.
825 + enable = mkEnableOption ''the headless Transmission BitTorrent daemon.
826
827 - Transmission daemon can be controlled via the RPC interface using
828 - transmission-remote or the WebUI (http://localhost:9091/ by default).
829 + Transmission daemon can be controlled via the RPC interface using
830 + transmission-remote, the WebUI (http://127.0.0.1:9091/ by default),
831 + or other clients like stig or tremc.
832
833 - Torrents are downloaded to ${downloadDir} by default and are
834 - accessible to users in the "transmission" group.
835 - '';
836 - };
837 + Torrents are downloaded to ${homeDir}/${downloadsDir} by default and are
838 + accessible to users in the "transmission" group'';
839
840 - settings = mkOption {
841 + settings = mkOption rec {
842 + # TODO: switch to types.config.json as prescribed by RFC0042 once it's implemented
843 type = types.attrs;
844 + apply = recursiveUpdate default;
845 default =
846 {
847 - download-dir = downloadDir;
848 - incomplete-dir = incompleteDir;
849 + download-dir = "${cfg.home}/${downloadsDir}";
850 + incomplete-dir = "${cfg.home}/${incompleteDir}";
851 incomplete-dir-enabled = true;
852 + peer-port = 51413;
853 + peer-port-random-high = 65535;
854 + peer-port-random-low = 49152;
855 + peer-port-random-on-start = false;
856 + rpc-bind-address = "127.0.0.1";
857 + rpc-port = 9091;
858 + script-torrent-done-enabled = false;
859 + script-torrent-done-filename = "";
860 + umask = 2; # 0o002 in decimal as expected by Transmission
861 + utp-enabled = true;
862 };
863 example =
864 {
865 @@ -56,8 +54,9 @@ in
866 rpc-whitelist = "127.0.0.1,192.168.*.*";
867 };
868 description = ''
869 - Attribute set whos fields overwrites fields in settings.json (each
870 - time the service starts). String values must be quoted, integer and
871 + Attribute set whose fields overwrites fields in
872 + <literal>.config/transmission-daemon/settings.json</literal>
873 + (each time the service starts). String values must be quoted, integer and
874 boolean values must not.
875
876 See https://github.com/transmission/transmission/wiki/Editing-Configuration-Files
877 @@ -70,22 +69,32 @@ in
878 default = "770";
879 example = "775";
880 description = ''
881 - The permissions to set for download-dir and incomplete-dir.
882 - They will be applied on every service start.
883 + The permissions set by <literal>systemd.activationScripts.transmission-daemon</literal>
884 + on the directories <link linkend="opt-services.transmission.settings">settings.download-dir</link>
885 + and <link linkend="opt-services.transmission.settings">settings.incomplete-dir</link>.
886 + Note that you may also want to change
887 + <link linkend="opt-services.transmission.settings">settings.umask</link>.
888 '';
889 };
890
891 port = mkOption {
892 - type = types.int;
893 - default = 9091;
894 - description = "TCP port number to run the RPC/web interface.";
895 + type = types.port;
896 + description = ''
897 + TCP port number to run the RPC/web interface.
898 +
899 + If instead you want to change the peer port,
900 + use <link linkend="opt-services.transmission.settings">settings.peer-port</link>
901 + or <link linkend="opt-services.transmission.settings">settings.peer-port-random-on-start</link>.
902 + '';
903 };
904
905 home = mkOption {
906 type = types.path;
907 - default = "/var/lib/transmission";
908 + default = homeDir;
909 description = ''
910 - The directory where transmission will create files.
911 + The directory where Transmission will create <literal>${settingsDir}</literal>.
912 + as well as <literal>${downloadsDir}/</literal> unless <link linkend="opt-services.transmission.settings">settings.download-dir</link> is changed,
913 + and <literal>${incompleteDir}/</literal> unless <link linkend="opt-services.transmission.settings">settings.incomplete-dir</link> is changed.
914 '';
915 };
916
917 @@ -100,32 +109,174 @@ in
918 default = "transmission";
919 description = "Group account under which Transmission runs.";
920 };
921 +
922 + credentialsFile = mkOption {
923 + type = types.path;
924 + description = ''
925 + Path to a JSON file to be merged with the settings.
926 + Useful to merge a file which is better kept out of the Nix store
927 + because it contains sensible data like <link linkend="opt-services.transmission.settings">settings.rpc-password</link>.
928 + '';
929 + default = "/dev/null";
930 + example = "/var/lib/secrets/transmission/settings.json";
931 + };
932 +
933 + openFirewall = mkEnableOption "opening of the peer port(s) in the firewall";
934 +
935 + performanceNetParameters = mkEnableOption ''tweaking of kernel parameters
936 + to open many more connections at the same time.
937 +
938 + Note that you may also want to increase
939 + <link linkend="opt-services.transmission.settings">settings.peer-limit-global</link>.
940 + And be aware that these settings are quite aggressive
941 + and might not suite your regular desktop use.
942 + For instance, SSH sessions may time out more easily'';
943 };
944 };
945
946 config = mkIf cfg.enable {
947 + # Note that using systemd.tmpfiles would not work here
948 + # because it would fail when creating a directory
949 + # with a different owner than its parent directory, by saying:
950 + # Detected unsafe path transition /home/foo → /home/foo/Downloads during canonicalization of /home/foo/Downloads.
951 + # When /home/foo is not owned by cfg.user.
952 + # Note also that using an ExecStartPre= wouldn't work either
953 + # because BindPaths= needs these directories before.
954 + system.activationScripts.transmission-daemon = ''
955 + install -D -d -m 700 '${cfg.home}/${settingsDir}'
956 + chown -R '${cfg.user}:${cfg.group}' ${cfg.home}/${settingsDir}
957 + install -D -d -m '${cfg.downloadDirPermissions}' -o '${cfg.user}' -g '${cfg.group}' '${cfg.settings.download-dir}'
958 + '' + optionalString cfg.settings.incomplete-dir-enabled ''
959 + install -D -d -m '${cfg.downloadDirPermissions}' -o '${cfg.user}' -g '${cfg.group}' '${cfg.settings.incomplete-dir}'
960 + '';
961 +
962 + assertions = [
963 + { assertion = builtins.match "^/.*" cfg.home != null;
964 + message = "`services.transmission.home' must be an absolute path.";
965 + }
966 + { assertion = types.path.check cfg.settings.download-dir;
967 + message = "`services.transmission.settings.download-dir' must be an absolute path.";
968 + }
969 + { assertion = types.path.check cfg.settings.incomplete-dir;
970 + message = "`services.transmission.settings.incomplete-dir' must be an absolute path.";
971 + }
972 + { assertion = cfg.settings.script-torrent-done-filename == "" || types.path.check cfg.settings.script-torrent-done-filename;
973 + message = "`services.transmission.settings.script-torrent-done-filename' must be an absolute path.";
974 + }
975 + { assertion = types.port.check cfg.settings.rpc-port;
976 + message = "${toString cfg.settings.rpc-port} is not a valid port number for `services.transmission.settings.rpc-port`.";
977 + }
978 + # In case both port and settings.rpc-port are explicitely defined: they must be the same.
979 + { assertion = !options.services.transmission.port.isDefined || cfg.port == cfg.settings.rpc-port;
980 + message = "`services.transmission.port' is not equal to `services.transmission.settings.rpc-port'";
981 + }
982 + ];
983 +
984 + services.transmission.settings =
985 + optionalAttrs options.services.transmission.port.isDefined { rpc-port = cfg.port; };
986 +
987 systemd.tmpfiles.rules = [
988 - "d '${homeDir}' 0770 '${cfg.user}' '${cfg.group}' - -"
989 - "d '${settingsDir}' 0700 '${cfg.user}' '${cfg.group}' - -"
990 - "d '${fullSettings.download-dir}' '${downloadDirPermissions}' '${cfg.user}' '${cfg.group}' - -"
991 - "d '${fullSettings.incomplete-dir}' '${downloadDirPermissions}' '${cfg.user}' '${cfg.group}' - -"
992 + # SETGUID is used to propagate cfg.group on intermediate paths
993 + # created by BindPaths=/BindReadOnlyPaths=
994 + "d ${rootDir} 2700 ${cfg.user} ${cfg.group} - -"
995 ];
996
997 systemd.services.transmission = {
998 description = "Transmission BitTorrent Service";
999 after = [ "network.target" ] ++ optional apparmor "apparmor.service";
1000 - requires = mkIf apparmor [ "apparmor.service" ];
1001 + requires = optional apparmor "apparmor.service";
1002 wantedBy = [ "multi-user.target" ];
1003 + environment.CURL_CA_BUNDLE = etc."ssl/certs/ca-certificates.crt".source;
1004
1005 - # 1) Only the "transmission" user and group have access to torrents.
1006 - # 2) Optionally update/force specific fields into the configuration file.
1007 - serviceConfig.ExecStartPre = preStart;
1008 - serviceConfig.ExecStart = "${pkgs.transmission}/bin/transmission-daemon -f --port ${toString config.services.transmission.port} --config-dir ${settingsDir}";
1009 - serviceConfig.ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
1010 - serviceConfig.User = cfg.user;
1011 - serviceConfig.Group = cfg.group;
1012 - # NOTE: transmission has an internal umask that also must be set (in settings.json)
1013 - serviceConfig.UMask = "0002";
1014 + serviceConfig = {
1015 + # Use "+" because credentialsFile may not be accessible to User= or Group=.
1016 + ExecStartPre = "+" + pkgs.writeShellScript "transmission-prestart" ''
1017 + set -eux
1018 + ${pkgs.jq}/bin/jq --slurp add ${settingsFile} '${cfg.credentialsFile}' |
1019 + install -D -m 600 -o '${cfg.user}' -g '${cfg.group}' /dev/stdin \
1020 + '${cfg.home}/${settingsDir}/settings.json'
1021 + '';
1022 + ExecStart = "${pkgs.transmission}/bin/transmission-daemon -f";
1023 + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
1024 + User = cfg.user;
1025 + Group = cfg.group;
1026 + # This allows to use the same paths download-dir/incomplete-dir
1027 + # (which appear in user's interfaces)
1028 + # while not requiring to give cfg.user access to their parent directories
1029 + # by using BindPaths=/BindReadOnlyPaths=
1030 + # Note that TemporaryFileSystem= could be used but not without adding
1031 + # some BindPaths=/BindReadOnlyPaths= only needed for ExecStartPre=
1032 + # because RootDirectoryStartOnly=true would not apply.
1033 + RootDirectory = rootDir;
1034 + RootDirectoryStartOnly = true;
1035 + MountAPIVFS = true;
1036 + # This is for BindPaths= and BindReadOnlyPaths=
1037 + # to allow Group= to traverse (g+x) directories
1038 + # they create in RootDirectory= (which has g+s from systemd.tmpfiles).
1039 + UMask = "0067";
1040 + BindPaths =
1041 + [ "${cfg.home}/${settingsDir}"
1042 + cfg.settings.download-dir
1043 + ] ++
1044 + optional cfg.settings.incomplete-dir-enabled
1045 + cfg.settings.incomplete-dir ++
1046 + optional (cfg.settings.script-torrent-done-enabled &&
1047 + cfg.settings.script-torrent-done-filename != "")
1048 + cfg.settings.script-torrent-done-filename;
1049 + BindReadOnlyPaths = [
1050 + # No confinement done of /nix/store here like in systemd-confinement.nix,
1051 + # an AppArmor profile is provided to get a confinement based upon paths and rights.
1052 + builtins.storeDir
1053 + "-/etc/hosts"
1054 + "-/etc/ld-nix.so.preload"
1055 + "-/etc/localtime"
1056 + ];
1057 + # The following options are only for optimizing:
1058 + # systemd-analyze security transmission
1059 + AmbientCapabilities = "";
1060 + CapabilityBoundingSet = "";
1061 + # ProtectClock= adds DeviceAllow=char-rtc r
1062 + DeviceAllow = "";
1063 + LockPersonality = true;
1064 + MemoryDenyWriteExecute = true;
1065 + NoNewPrivileges = true;
1066 + PrivateDevices = true;
1067 + PrivateMounts = true;
1068 + PrivateNetwork = false;
1069 + PrivateTmp = true;
1070 + PrivateUsers = true;
1071 + ProtectClock = true;
1072 + ProtectControlGroups = true;
1073 + # ProtectHome=true would not allow BindPaths= to work accross /home,
1074 + # and ProtectHome=tmpfs would break statfs(),
1075 + # preventing transmission-daemon to report the available free space.
1076 + # However, RootDirectory= is used, so this is not a security concern
1077 + # since there would be nothing in /home but any BindPaths= wanted by the user.
1078 + ProtectHome = "read-only";
1079 + ProtectHostname = true;
1080 + ProtectKernelLogs = true;
1081 + ProtectKernelModules = true;
1082 + ProtectKernelTunables = true;
1083 + ProtectSystem = "strict";
1084 + RemoveIPC = true;
1085 + # AF_UNIX may become usable one day:
1086 + # https://github.com/transmission/transmission/issues/441
1087 + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
1088 + RestrictNamespaces = true;
1089 + RestrictRealtime = true;
1090 + RestrictSUIDSGID = true;
1091 + SystemCallFilter = [
1092 + "@system-service"
1093 + # Groups in @system-service which do not contain a syscall
1094 + # listed by perf -e 'syscalls:sys_enter_*' transmission-daemon -f
1095 + # in tests, and seem likely not necessary for transmission-daemon.
1096 + "~@aio" "~@chown" "~@keyring" "~@memlock" "~@resources" "~@setuid" "~@timer"
1097 + # In the @privileged group, but reached when querying infos through RPC (eg. with stig).
1098 + "quotactl"
1099 + ];
1100 + SystemCallArchitectures = "native";
1101 + SystemCallErrorNumber = "EPERM";
1102 + };
1103 };
1104
1105 # It's useful to have transmission in path, e.g. for remote control
1106 @@ -133,70 +284,135 @@ in
1107
1108 users.users = optionalAttrs (cfg.user == "transmission") ({
1109 transmission = {
1110 - name = "transmission";
1111 group = cfg.group;
1112 uid = config.ids.uids.transmission;
1113 description = "Transmission BitTorrent user";
1114 - home = homeDir;
1115 - createHome = true;
1116 + home = cfg.home;
1117 };
1118 });
1119
1120 users.groups = optionalAttrs (cfg.group == "transmission") ({
1121 transmission = {
1122 - name = "transmission";
1123 gid = config.ids.gids.transmission;
1124 };
1125 });
1126
1127 - # AppArmor profile
1128 - security.apparmor.profiles = mkIf apparmor [
1129 - (pkgs.writeText "apparmor-transmission-daemon" ''
1130 - #include <tunables/global>
1131 + networking.firewall = mkIf cfg.openFirewall (
1132 + if cfg.settings.peer-port-random-on-start
1133 + then
1134 + { allowedTCPPortRanges =
1135 + [ { from = cfg.settings.peer-port-random-low;
1136 + to = cfg.settings.peer-port-random-high;
1137 + }
1138 + ];
1139 + allowedUDPPortRanges =
1140 + [ { from = cfg.settings.peer-port-random-low;
1141 + to = cfg.settings.peer-port-random-high;
1142 + }
1143 + ];
1144 + }
1145 + else
1146 + { allowedTCPPorts = [ cfg.settings.peer-port ];
1147 + allowedUDPPorts = [ cfg.settings.peer-port ];
1148 + }
1149 + );
1150 +
1151 + boot.kernel.sysctl =
1152 + # Transmission uses a single UDP socket in order to implement multiple uTP sockets,
1153 + # and thus expects large kernel buffers for the UDP socket,
1154 + # at least up to the values hardcoded here:
1155 + # https://trac.transmissionbt.com/browser/trunk/libtransmission/tr-udp.c?rev=11956.
1156 + mkIf cfg.settings.utp-enabled {
1157 + "net.core.rmem_max" = mkDefault "4194304"; # 4MB
1158 + "net.core.wmem_max" = mkDefault "1048576"; # 1MB
1159 + } // mkIf cfg.performanceNetParameters {
1160 + # Increase the number of available source (local) TCP and UDP ports to 49151.
1161 + # Usual default is 32768 60999, ie. 28231 ports.
1162 + # Find out your current usage with: ss -s
1163 + "net.ipv4.ip_local_port_range" = "16384 65535";
1164 + # Timeout faster generic TCP states.
1165 + # Usual default is 600.
1166 + # Find out your current usage with: watch -n 1 netstat -nptuo
1167 + "net.netfilter.nf_conntrack_generic_timeout" = 60;
1168 + # Timeout faster established but inactive connections.
1169 + # Usual default is 432000.
1170 + "net.netfilter.nf_conntrack_tcp_timeout_established" = 600;
1171 + # Clear immediately TCP states after timeout.
1172 + # Usual default is 120.
1173 + "net.netfilter.nf_conntrack_tcp_timeout_time_wait" = 1;
1174 + # Increase the number of trackable connections.
1175 + # Usual default is 262144.
1176 + # Find out your current usage with: conntrack -C
1177 + "net.netfilter.nf_conntrack_max" = 1048576;
1178 + };
1179 +
1180 + security.apparmor.policies."bin/transmission-daemon".profile = ''
1181 + include <tunables/global>
1182
1183 ${pkgs.transmission}/bin/transmission-daemon {
1184 - #include <abstractions/base>
1185 - #include <abstractions/nameservice>
1186 + include <abstractions/base>
1187 + include <abstractions/nameservice>
1188
1189 - ${getLib pkgs.glibc}/lib/*.so mr,
1190 - ${getLib pkgs.libevent}/lib/libevent*.so* mr,
1191 - ${getLib pkgs.curl}/lib/libcurl*.so* mr,
1192 - ${getLib pkgs.openssl}/lib/libssl*.so* mr,
1193 - ${getLib pkgs.openssl}/lib/libcrypto*.so* mr,
1194 - ${getLib pkgs.zlib}/lib/libz*.so* mr,
1195 - ${getLib pkgs.libssh2}/lib/libssh2*.so* mr,
1196 - ${getLib pkgs.systemd}/lib/libsystemd*.so* mr,
1197 - ${getLib pkgs.xz}/lib/liblzma*.so* mr,
1198 - ${getLib pkgs.libgcrypt}/lib/libgcrypt*.so* mr,
1199 - ${getLib pkgs.libgpgerror}/lib/libgpg-error*.so* mr,
1200 - ${getLib pkgs.nghttp2}/lib/libnghttp2*.so* mr,
1201 - ${getLib pkgs.c-ares}/lib/libcares*.so* mr,
1202 - ${getLib pkgs.libcap}/lib/libcap*.so* mr,
1203 - ${getLib pkgs.attr}/lib/libattr*.so* mr,
1204 - ${getLib pkgs.lz4}/lib/liblz4*.so* mr,
1205 - ${getLib pkgs.libkrb5}/lib/lib*.so* mr,
1206 - ${getLib pkgs.keyutils}/lib/libkeyutils*.so* mr,
1207 - ${getLib pkgs.utillinuxMinimal.out}/lib/libblkid.so.* mr,
1208 - ${getLib pkgs.utillinuxMinimal.out}/lib/libmount.so.* mr,
1209 - ${getLib pkgs.utillinuxMinimal.out}/lib/libuuid.so.* mr,
1210 - ${getLib pkgs.gcc.cc.lib}/lib/libstdc++.so.* mr,
1211 - ${getLib pkgs.gcc.cc.lib}/lib/libgcc_s.so.* mr,
1212 + mr ${getLib pkgs.stdenv.cc.cc}/lib/*.so*,
1213 + mr ${getLib pkgs.stdenv.cc.libc}/lib/*.so*,
1214 + mr ${getLib pkgs.attr}/lib/libattr*.so*,
1215 + mr ${getLib pkgs.c-ares}/lib/libcares*.so*,
1216 + mr ${getLib pkgs.curl}/lib/libcurl*.so*,
1217 + mr ${getLib pkgs.keyutils}/lib/libkeyutils*.so*,
1218 + mr ${getLib pkgs.libcap}/lib/libcap*.so*,
1219 + mr ${getLib pkgs.libevent}/lib/libevent*.so*,
1220 + mr ${getLib pkgs.libgcrypt}/lib/libgcrypt*.so*,
1221 + mr ${getLib pkgs.libgpgerror}/lib/libgpg-error*.so*,
1222 + mr ${getLib pkgs.libkrb5}/lib/lib*.so*,
1223 + mr ${getLib pkgs.libssh2}/lib/libssh2*.so*,
1224 + mr ${getLib pkgs.lz4}/lib/liblz4*.so*,
1225 + mr ${getLib pkgs.nghttp2}/lib/libnghttp2*.so*,
1226 + mr ${getLib pkgs.openssl}/lib/libcrypto*.so*,
1227 + mr ${getLib pkgs.openssl}/lib/libssl*.so*,
1228 + mr ${getLib pkgs.systemd}/lib/libsystemd*.so*,
1229 + mr ${getLib pkgs.utillinuxMinimal.out}/lib/libblkid.so*,
1230 + mr ${getLib pkgs.utillinuxMinimal.out}/lib/libmount.so*,
1231 + mr ${getLib pkgs.utillinuxMinimal.out}/lib/libuuid.so*,
1232 + mr ${getLib pkgs.xz}/lib/liblzma*.so*,
1233 + mr ${getLib pkgs.zlib}/lib/libz*.so*,
1234
1235 - @{PROC}/sys/kernel/random/uuid r,
1236 - @{PROC}/sys/vm/overcommit_memory r,
1237 + r @{PROC}/sys/kernel/random/uuid,
1238 + r @{PROC}/sys/vm/overcommit_memory,
1239 + # @{pid} is not a kernel variable yet but a regexp
1240 + #r @{PROC}/@{pid}/environ,
1241 + r @{PROC}/@{pid}/mounts,
1242 + rwk /tmp/tr_session_id_*,
1243
1244 - ${pkgs.openssl.out}/etc/** r,
1245 - ${pkgs.transmission}/share/transmission/** r,
1246 + r ${pkgs.openssl.out}/etc/**,
1247 + r ${config.systemd.services.transmission.environment.CURL_CA_BUNDLE},
1248 + r ${pkgs.transmission}/share/transmission/**,
1249
1250 - owner ${settingsDir}/** rw,
1251 -
1252 - ${fullSettings.download-dir}/** rw,
1253 - ${optionalString fullSettings.incomplete-dir-enabled ''
1254 - ${fullSettings.incomplete-dir}/** rw,
1255 + owner rw ${cfg.home}/${settingsDir}/**,
1256 + rw ${cfg.settings.download-dir}/**,
1257 + ${optionalString cfg.settings.incomplete-dir-enabled ''
1258 + rw ${cfg.settings.incomplete-dir}/**,
1259 ''}
1260 + profile dirs {
1261 + rw ${cfg.settings.download-dir}/**,
1262 + ${optionalString cfg.settings.incomplete-dir-enabled ''
1263 + rw ${cfg.settings.incomplete-dir}/**,
1264 + ''}
1265 + }
1266 +
1267 + ${optionalString (cfg.settings.script-torrent-done-enabled &&
1268 + cfg.settings.script-torrent-done-filename != "") ''
1269 + # Stack transmission_directories profile on top of
1270 + # any existing profile for script-torrent-done-filename
1271 + # FIXME: to be tested as I'm not sure it works well with NoNewPrivileges=
1272 + # https://gitlab.com/apparmor/apparmor/-/wikis/AppArmorStacking#seccomp-and-no_new_privs
1273 + px ${cfg.settings.script-torrent-done-filename} -> &@{dirs},
1274 + ''}
1275 +
1276 + # FIXME: enable customizing using https://github.com/NixOS/nixpkgs/pull/93457
1277 + # include <local/transmission-daemon>
1278 }
1279 - '')
1280 - ];
1281 + '';
1282 };
1283
1284 + meta.maintainers = with lib.maintainers; [ julm ];
1285 }
1286 diff --git a/nixos/modules/virtualisation/lxc.nix b/nixos/modules/virtualisation/lxc.nix
1287 index f484d5ee59a..a2f4a9867c6 100644
1288 --- a/nixos/modules/virtualisation/lxc.nix
1289 +++ b/nixos/modules/virtualisation/lxc.nix
1290 @@ -74,9 +74,13 @@ in
1291 systemd.tmpfiles.rules = [ "d /var/lib/lxc/rootfs 0755 root root -" ];
1292
1293 security.apparmor.packages = [ pkgs.lxc ];
1294 - security.apparmor.profiles = [
1295 - "${pkgs.lxc}/etc/apparmor.d/lxc-containers"
1296 - "${pkgs.lxc}/etc/apparmor.d/usr.bin.lxc-start"
1297 - ];
1298 + security.apparmor.policies = {
1299 + "bin/lxc-start".profile = ''
1300 + #include ${pkgs.lxc}/etc/apparmor.d/usr.bin.lxc-start
1301 + '';
1302 + "lxc-containers".profile = ''
1303 + #include ${pkgs.lxc}/etc/apparmor.d/lxc-containers
1304 + '';
1305 + };
1306 };
1307 }
1308 diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix
1309 index 3958fc2c1d7..84c8e88f8b4 100644
1310 --- a/nixos/modules/virtualisation/lxd.nix
1311 +++ b/nixos/modules/virtualisation/lxd.nix
1312 @@ -93,11 +93,15 @@ in
1313
1314 security.apparmor = {
1315 enable = true;
1316 - profiles = [
1317 - "${cfg.lxcPackage}/etc/apparmor.d/usr.bin.lxc-start"
1318 - "${cfg.lxcPackage}/etc/apparmor.d/lxc-containers"
1319 - ];
1320 packages = [ cfg.lxcPackage ];
1321 + policies = {
1322 + "bin/lxc-start".profile = ''
1323 + #include ${cfg.lxcPackage}/etc/apparmor.d/usr.bin.lxc-start
1324 + '';
1325 + "lxc-containers".profile = ''
1326 + #include ${cfg.lxcPackage}/etc/apparmor.d/lxc-containers
1327 + '';
1328 + };
1329 };
1330
1331 systemd.services.lxd = {
1332 diff --git a/nixos/tests/bittorrent.nix b/nixos/tests/bittorrent.nix
1333 index 0a97d5556a2..c195b60cd56 100644
1334 --- a/nixos/tests/bittorrent.nix
1335 +++ b/nixos/tests/bittorrent.nix
1336 @@ -19,6 +19,7 @@ let
1337 externalClient2Address = "80.100.100.2";
1338 externalTrackerAddress = "80.100.100.3";
1339
1340 + download-dir = "/var/lib/transmission/Downloads";
1341 transmissionConfig = { ... }: {
1342 environment.systemPackages = [ pkgs.transmission ];
1343 services.transmission = {
1344 @@ -26,6 +27,7 @@ let
1345 settings = {
1346 dht-enabled = false;
1347 message-level = 3;
1348 + inherit download-dir;
1349 };
1350 };
1351 };
1352 @@ -117,12 +119,12 @@ in
1353 router.wait_for_unit("miniupnpd")
1354
1355 # Create the torrent.
1356 - tracker.succeed("mkdir /tmp/data")
1357 + tracker.succeed("mkdir ${download-dir}/data")
1358 tracker.succeed(
1359 - "cp ${file} /tmp/data/test.tar.bz2"
1360 + "cp ${file} ${download-dir}/data/test.tar.bz2"
1361 )
1362 tracker.succeed(
1363 - "transmission-create /tmp/data/test.tar.bz2 --private --tracker http://${externalTrackerAddress}:6969/announce --outfile /tmp/test.torrent"
1364 + "transmission-create ${download-dir}/data/test.tar.bz2 --private --tracker http://${externalTrackerAddress}:6969/announce --outfile /tmp/test.torrent"
1365 )
1366 tracker.succeed("chmod 644 /tmp/test.torrent")
1367
1368 @@ -133,18 +135,16 @@ in
1369
1370 # Start the initial seeder.
1371 tracker.succeed(
1372 - "transmission-remote --add /tmp/test.torrent --no-portmap --no-dht --download-dir /tmp/data"
1373 + "transmission-remote --add /tmp/test.torrent --no-portmap --no-dht --download-dir ${download-dir}/data"
1374 )
1375
1376 # Now we should be able to download from the client behind the NAT.
1377 tracker.wait_for_unit("httpd")
1378 client1.wait_for_unit("network-online.target")
1379 + client1.succeed("transmission-remote --add http://${externalTrackerAddress}/test.torrent >&2 &")
1380 + client1.wait_for_file("${download-dir}/test.tar.bz2")
1381 client1.succeed(
1382 - "transmission-remote --add http://${externalTrackerAddress}/test.torrent --download-dir /tmp >&2 &"
1383 - )
1384 - client1.wait_for_file("/tmp/test.tar.bz2")
1385 - client1.succeed(
1386 - "cmp /tmp/test.tar.bz2 ${file}"
1387 + "cmp ${download-dir}/test.tar.bz2 ${file}"
1388 )
1389
1390 # Bring down the initial seeder.
1391 @@ -154,11 +154,11 @@ in
1392 # the first client created a NAT hole in the router.
1393 client2.wait_for_unit("network-online.target")
1394 client2.succeed(
1395 - "transmission-remote --add http://${externalTrackerAddress}/test.torrent --no-portmap --no-dht --download-dir /tmp >&2 &"
1396 + "transmission-remote --add http://${externalTrackerAddress}/test.torrent --no-portmap --no-dht >&2 &"
1397 )
1398 - client2.wait_for_file("/tmp/test.tar.bz2")
1399 + client2.wait_for_file("${download-dir}/test.tar.bz2")
1400 client2.succeed(
1401 - "cmp /tmp/test.tar.bz2 ${file}"
1402 + "cmp ${download-dir}/test.tar.bz2 ${file}"
1403 )
1404 '';
1405 })
1406 diff --git a/pkgs/os-specific/linux/apparmor/default.nix b/pkgs/os-specific/linux/apparmor/default.nix
1407 index 66c2582603c..0205abc290b 100644
1408 --- a/pkgs/os-specific/linux/apparmor/default.nix
1409 +++ b/pkgs/os-specific/linux/apparmor/default.nix
1410 @@ -10,6 +10,10 @@
1411 , pam
1412 , libnotify
1413 , buildPackages
1414 +, coreutils
1415 +, gnugrep
1416 +, gnused
1417 +, writeShellScript
1418 }:
1419
1420 let
1421 @@ -38,6 +42,22 @@ let
1422 sha256 = "0xw028iqp69j9mxv0kbwraplgkj5i5djdlgf0anpkc5cdbsf96r9";
1423 };
1424
1425 + aa-teardown = writeShellScript "aa-teardown" ''
1426 + SECURITYFS=/sys/kernel/security
1427 + SFS_MOUNTPOINT="$SECURITYFS/apparmor"
1428 + ${gnused}/bin/sed -e "s/ (\(enforce\|complain\))$//" "$SFS_MOUNTPOINT/profiles" | \
1429 + LC_COLLATE=C ${coreutils}/bin/sort | ${gnugrep}/bin/grep -v // | {
1430 + while read -r profile ; do
1431 + printf "%s" "$profile" > "$SFS_MOUNTPOINT/.remove"
1432 + rc=$?
1433 + if [ "$rc" -ne 0 ] ; then
1434 + retval=$rc
1435 + fi
1436 + done
1437 + exit "''${retval:-0}"
1438 + }
1439 + '';
1440 +
1441 prePatchCommon = ''
1442 substituteInPlace ./common/Make.rules --replace "/usr/bin/pod2man" "${buildPackages.perl}/bin/pod2man"
1443 substituteInPlace ./common/Make.rules --replace "/usr/bin/pod2html" "${buildPackages.perl}/bin/pod2html"
1444 @@ -142,6 +162,8 @@ let
1445 # aa-notify checks its name and does not work named ".aa-notify-wrapped"
1446 mv $out/bin/aa-notify $out/bin/aa-notify-wrapped
1447 makeWrapper ${perl}/bin/perl $out/bin/aa-notify --set PERL5LIB ${libapparmor}/${perl.libPrefix} --add-flags $out/bin/aa-notify-wrapped
1448 +
1449 + ln -s ${aa-teardown} $out/bin/aa-teardown
1450 '';
1451
1452 inherit doCheck;