1 diff --git a/nixos/modules/config/fonts/fontconfig.nix b/nixos/modules/config/fonts/fontconfig.nix
2 index ac2a024eaa8..0519812df88 100644
3 --- a/nixos/modules/config/fonts/fontconfig.nix
4 +++ b/nixos/modules/config/fonts/fontconfig.nix
5 @@ -489,6 +489,38 @@ in
7 environment.systemPackages = [ pkgs.fontconfig ];
8 environment.etc.fonts.source = "${fontconfigEtc}/etc/fonts/";
9 + security.apparmor.includes."abstractions/fonts" = ''
11 + r ${supportFontsConf},
12 + r ${latestPkg.out}/etc/fonts/fonts.conf,
14 + # fontconfig default config files
15 + r ${supportPkg.out}/etc/fonts/conf.d/*.conf,
16 + r ${latestPkg.out}/etc/fonts/conf.d/*.conf,
18 + # 00-nixos-cache.conf
19 + r ${cacheConfSupport},
20 + r ${cacheConfLatest},
22 + # 10-nixos-rendering.conf
25 + # local.conf (indirect priority 51)
26 + ${optionalString (cfg.localConf != "") ''
30 + # 52-nixos-default-fonts.conf
31 + r ${defaultFontsConf},
33 + # 53-no-bitmaps.conf
36 + ${optionalString (!cfg.allowType1) ''
37 + # 53-nixos-reject-type1.conf
42 (mkIf (cfg.enable && !cfg.penultimate.enable) {
43 fonts.fontconfig.confPackages = [ confPkg ];
44 diff --git a/nixos/modules/security/apparmor-suid.nix b/nixos/modules/security/apparmor-suid.nix
45 index 6c479e070e2..e6d9467f296 100644
46 --- a/nixos/modules/security/apparmor-suid.nix
47 +++ b/nixos/modules/security/apparmor-suid.nix
48 @@ -21,9 +21,9 @@ with lib;
51 config = mkIf (cfg.confineSUIDApplications) {
52 - security.apparmor.profiles = [ (pkgs.writeText "ping" ''
53 + security.apparmor.policies."bin/ping".profile = ''
54 #include <tunables/global>
55 - /run/wrappers/bin/ping {
56 + /run/wrappers/wrappers.*/ping {
57 #include <abstractions/base>
58 #include <abstractions/consoles>
59 #include <abstractions/nameservice>
60 @@ -32,10 +32,19 @@ with lib;
64 - ${pkgs.stdenv.cc.libc.out}/lib/*.so mr,
65 - ${pkgs.libcap.lib}/lib/libcap.so* mr,
66 - ${pkgs.attr.out}/lib/libattr.so* mr,
67 + ${getLib pkgs.stdenv.cc.cc}/lib/*.so* mr,
68 + ${getLib pkgs.stdenv.cc.libc}/lib/*.so* mr,
69 + ${getLib pkgs.stdenv.cc.libc}/lib/gconv/gconv-modules r,
70 + ${getLib pkgs.glibcLocales}/lib/locale/locale-archive r,
71 + ${getLib pkgs.attr.out}/lib/libattr.so* mr,
72 + ${getLib pkgs.libcap.lib}/lib/libcap.so* mr,
73 + ${getLib pkgs.libcap_ng}/lib/libcap-ng.so* mr,
74 + ${getLib pkgs.libidn2}/lib/libidn2.so* mr,
75 + ${getLib pkgs.libunistring}/lib/libunistring.so* mr,
76 + ${getLib pkgs.nettle}/lib/libnettle.so* mr,
78 + #@{PROC}/@{pid}/environ r,
79 + /run/wrappers/wrappers.*/ping.real r,
80 ${pkgs.iputils}/bin/ping mixr,
83 @@ -43,7 +52,7 @@ with lib;
84 ## Site-specific additions and overrides. See local/README for details.
85 ##include <local/bin.ping>
92 diff --git a/nixos/modules/security/apparmor.nix b/nixos/modules/security/apparmor.nix
93 index cfc65b347bc..f9abb18afd2 100644
94 --- a/nixos/modules/security/apparmor.nix
95 +++ b/nixos/modules/security/apparmor.nix
97 { config, lib, pkgs, ... }:
100 - inherit (lib) mkIf mkOption types concatMapStrings;
101 + inherit (builtins) head match readFile;
102 + inherit (lib) types;
103 + inherit (config.environment) etc;
104 cfg = config.security.apparmor;
105 + mkDisableOption = name: lib.mkEnableOption name // {
113 - security.apparmor = {
114 - enable = mkOption {
117 - description = "Enable the AppArmor Mandatory Access Control system.";
119 - profiles = mkOption {
120 - type = types.listOf types.path;
122 - description = "List of files containing AppArmor profiles.";
124 - packages = mkOption {
125 - type = types.listOf types.package;
127 - description = "List of packages to be added to apparmor's include path";
132 + (lib.mkRemovedOptionModule [ "security" "apparmor" "profiles" ] "Please use the new option: `security.apparmor.policies'.")
133 + apparmor/profiles.nix
136 + security.apparmor = {
137 + enable = lib.mkEnableOption "the AppArmor Mandatory Access Control system";
138 + policies = lib.mkOption {
142 + type = types.attrsOf (types.submodule ({ name, config, ... }: {
144 + enable = mkDisableOption "loading of the profile into the kernel";
145 + enforce = mkDisableOption "enforcing of the policy or only complain in the logs";
146 + profile = lib.mkOption {
147 + description = "The policy of the profile.";
148 + type = types.lines;
149 + apply = pkgs.writeText name;
155 + includes = lib.mkOption {
156 + type = types.attrsOf types.lines;
159 + List of paths to be added to AppArmor's searched paths
160 + when resolving absolute #include directives.
162 + apply = lib.mapAttrs pkgs.writeText;
164 + packages = lib.mkOption {
165 + type = types.listOf types.package;
167 + description = "List of packages to be added to AppArmor's include path";
169 + enableCache = lib.mkEnableOption ''caching of AppArmor policies
170 + in <literal>/var/cache/apparmor/</literal>.
171 + Beware that AppArmor policies almost always contain Nix store paths,
172 + and thus produce at each change of these paths
173 + a new cached version accumulating in the cache.
175 + killUnconfinedConfinables = mkDisableOption ''killing of processes
176 + which have an AppArmor profile enabled
177 + (in <link linkend="opt-security.apparmor.policies">policies</link>)
178 + but are not confined (because AppArmor can only confine new processes).
183 - config = mkIf cfg.enable {
184 - environment.systemPackages = [ pkgs.apparmor-utils ];
185 + config = lib.mkIf cfg.enable {
186 + environment.systemPackages = [ pkgs.apparmor-utils ];
187 + environment.etc."apparmor.d".source = pkgs.linkFarm "apparmor.d" (
188 + lib.mapAttrsToList (name: p: {inherit name; path=p.profile;}) cfg.policies ++
189 + lib.mapAttrsToList (name: path: {inherit name path;}) cfg.includes
191 + environment.etc."apparmor/parser.conf".text = ''
192 + ${if cfg.enableCache then "write-cache" else "skip-cache"}
193 + cache-loc /var/cache/apparmor
194 + Include /etc/apparmor.d
196 + lib.concatMapStrings (p: "Include ${p}/etc/apparmor.d\n") cfg.packages;
197 + environment.etc."apparmor/logprof.conf".text = ''
199 + profiledir = /etc/apparmor.d
200 + inactive_profiledir = ${pkgs.apparmor-profiles}/share/apparmor/extra-profiles
201 + logfiles = /var/log/audit/audit.log /var/log/syslog /var/log/messages
203 - boot.kernelParams = [ "apparmor=1" "security=apparmor" ];
204 + parser = ${pkgs.apparmor-parser}/bin/apparmor_parser
205 + ldd = ${pkgs.glibc.bin}/bin/ldd
206 + logger = ${pkgs.utillinux}/bin/logger
208 - systemd.services.apparmor = let
209 - paths = concatMapStrings (s: " -I ${s}/etc/apparmor.d")
210 - ([ pkgs.apparmor-profiles ] ++ cfg.packages);
212 - after = [ "local-fs.target" ];
213 - before = [ "sysinit.target" ];
214 - wantedBy = [ "multi-user.target" ];
216 - DefaultDependencies = "no";
220 - RemainAfterExit = "yes";
221 - ExecStart = map (p:
222 - ''${pkgs.apparmor-parser}/bin/apparmor_parser -rKv ${paths} "${p}"''
225 - ''${pkgs.apparmor-parser}/bin/apparmor_parser -Rv "${p}"''
227 - ExecReload = map (p:
228 - ''${pkgs.apparmor-parser}/bin/apparmor_parser --reload ${paths} "${p}"''
233 + # customize how file ownership permissions are presented
235 + # 1 - default of what ever mode the log reported
236 + # 2 - force the new permissions to be user
237 + # 3 - force all perms on the rule to be user
238 + default_owner_prompt = 1
240 + # custom directory locations to look for #includes
242 + # each name should be a valid directory containing possible #include
243 + # candidate files under the profile dir which by default is /etc/apparmor.d.
245 + # So an entry of my-includes will allow /etc/apparmor.d/my-includes to
246 + # be used by the yast UI and profiling tools as a source of #include
251 + ${pkgs.runtimeShell} = icnu
252 + ${pkgs.bashInteractive}/bin/sh = icnu
253 + ${pkgs.bashInteractive}/bin/bash = icnu
254 + '' + head (match "^.*\\[qualifiers](.*)" # Drop the original [settings] section.
255 + (readFile "${pkgs.apparmor-utils}/etc/apparmor/logprof.conf"));
257 + boot.kernelParams = [ "apparmor=1" "security=apparmor" ];
259 + systemd.services.apparmor = {
262 + "systemd-journald-audit.socket"
264 + before = [ "sysinit.target" ];
265 + wantedBy = [ "multi-user.target" ];
266 + restartTriggers = [
267 + etc."apparmor/parser.conf".source
268 + etc."apparmor.d".source
271 + Description="Load AppArmor policies";
272 + DefaultDependencies = "no";
273 + ConditionSecurity = "apparmor";
275 + # Reloading instead of restarting enables to load new AppArmor profiles
276 + # without necessarily restarting all services which have Requires=apparmor.service
278 + # - Adding or replacing into the kernel profiles enabled in cfg.policies
279 + # (because AppArmor can do that without stopping the processes already confined).
280 + # - Removing from the kernel any profile whose name is not
281 + # one of the names within the content of the profiles in cfg.policies.
282 + # - Killing the processes which are unconfined but now have a profile loaded
283 + # (because AppArmor can only confine new processes).
284 + reloadIfChanged = true;
285 + # Avoid searchs in /usr/share/locale/
286 + environment.LANG="C";
287 + serviceConfig = let
288 + enabledPolicies = lib.attrValues (lib.filterAttrs (n: p: p.enable) cfg.policies);
289 + removeDisabledProfiles = pkgs.writeShellScript "apparmor-remove" ''
292 + enabledProfiles=$(mktemp)
293 + loadedProfiles=$(mktemp)
294 + trap "rm -f $enabledProfiles $loadedProfiles" EXIT
296 + ${pkgs.apparmor-parser}/bin/apparmor_parser --names /dev/null ${
297 + lib.concatMapStrings (p: "\\\n "+p.profile) enabledPolicies} |
298 + sort -u >"$enabledProfiles"
300 + sed -e "s/ (\(enforce\|complain\))$//" /sys/kernel/security/apparmor/profiles |
301 + sort -u >"$loadedProfiles"
303 + comm -23 "$loadedProfiles" "$enabledProfiles" |
304 + while IFS=$'\n\r' read -r profile
305 + do printf %s "$profile" >/sys/kernel/security/apparmor/.remove
308 + killUnconfinedConfinables = pkgs.writeShellScript "apparmor-kill" ''
310 + ${pkgs.apparmor-utils}/bin/aa-status --json |
311 + ${pkgs.jq}/bin/jq --raw-output '.processes | .[] | .[] | select (.status == "unconfined") | .pid' |
312 + xargs --verbose --no-run-if-empty --delimiter='\n' \
315 + commonOpts = p: "--verbose --show-cache ${lib.optionalString (!p.enforce) "--complain "}${p.profile}";
318 + RemainAfterExit = "yes";
319 + ExecStartPre = "${pkgs.apparmor-utils}/bin/aa-teardown";
320 + ExecStart = map (p: "${pkgs.apparmor-parser}/bin/apparmor_parser --add ${commonOpts p}") enabledPolicies;
321 + ExecStartPost = lib.optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
323 + map (p: "${pkgs.apparmor-parser}/bin/apparmor_parser --replace ${commonOpts p}") enabledPolicies ++
324 + [ removeDisabledProfiles ] ++
325 + lib.optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
326 + ExecStop = "${pkgs.apparmor-utils}/bin/aa-teardown";
327 + CacheDirectory = [ "apparmor" ];
328 + CacheDirectoryMode = "0700";
333 + meta.maintainers = with lib.maintainers; [ julm ];
335 diff --git a/nixos/modules/security/apparmor/profiles.nix b/nixos/modules/security/apparmor/profiles.nix
337 index 00000000000..7e33e630798
339 +++ b/nixos/modules/security/apparmor/profiles.nix
341 +{ config, lib, pkgs, ... }:
343 + inherit (builtins) attrNames hasAttr isAttrs;
344 + inherit (lib) getLib;
345 + inherit (config.environment) etc;
347 + let go = {path ? null, mode ? "r", trail ? ""}:
348 + lib.optionalString (hasAttr path etc)
349 + "${mode} ${config.environment.etc."${path}".source}${trail},";
352 + else go {path=arg;};
355 +config.security.apparmor.packages = [ pkgs.apparmor-profiles ];
356 +# FIXME: most of the etcRule calls below have been
357 +# written systematically by converting from apparmor-profiles's profiles
358 +# without testing nor deep understanding of their uses,
359 +# and thus may need more rules or can have less rules;
360 +# this remains to me determined case by case,
361 +# some may even be completely useless.
362 +config.security.apparmor.includes = {
363 + # This one is included by <tunables/global>
364 + # which is usualy included before any profile.
365 + "abstractions/tunables/alias" = ''
366 + alias /bin -> /run/current-system/sw/bin,
367 + # Unfortunately /etc is mainly built using symlinks,
368 + # thus aliasing does not work.
369 + #alias /etc -> /run/current-system/etc,
370 + alias /lib/modules -> /run/current-system/kernel/lib/modules,
371 + alias /sbin -> /run/current-system/sw/sbin,
372 + alias /usr -> /run/current-system/sw,
374 + "abstractions/audio" = ''
375 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/audio
376 + ${etcRule "asound.conf"}
377 + ${etcRule "esound/esd.conf"}
378 + ${etcRule "libao.conf"}
379 + ${etcRule {path="pulse"; trail="/";}}
380 + ${etcRule {path="pulse"; trail="/**";}}
381 + ${etcRule {path="sound"; trail="/";}}
382 + ${etcRule {path="sound"; trail="/**";}}
383 + ${etcRule {path="alsa/conf.d"; trail="/";}}
384 + ${etcRule {path="alsa/conf.d"; trail="/*";}}
385 + ${etcRule "openal/alsoft.conf"}
386 + ${etcRule "wildmidi/wildmidi.conf"}
388 + # FIXME: security.pam configures more .so than allowed here,
389 + # but has many tests to decide what .so to use,
390 + # so it would be simpler to let security.pam add those .so
391 + # to the present security.apparmor.includes."abstractions/authentication"
392 + "abstractions/authentication" = ''
393 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/authentication
394 + ${etcRule "nologin"}
395 + ${lib.concatMapStringsSep "\n"
396 + (name: "r ${etc."pam.d/${name}".source},")
397 + (attrNames config.security.pam.services)}
398 + mr ${getLib pkgs.pam}/lib/security/pam_filter/*,
399 + mr ${getLib pkgs.pam}/lib/security/pam_*.so,
400 + r ${getLib pkgs.pam}/lib/security/,
401 + ${etcRule "securetty"}
402 + ${etcRule {path="security"; trail="/*";}}
403 + ${etcRule "shadow"}
404 + ${etcRule "gshadow"}
405 + ${etcRule "pwdb.conf"}
406 + ${etcRule "default/passwd"}
407 + ${etcRule "login.defs"}
409 + "abstractions/base" = ''
410 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/base
411 + r ${pkgs.stdenv.cc.libc}/share/locale/**,
412 + r ${pkgs.stdenv.cc.libc}/share/locale.alias,
413 + ${etcRule "localtime"}
414 + r /etc/ld-nix.so.preload,
415 + ${etcRule "ld-nix.so.preload"}
416 + ${lib.concatMapStrings (p: lib.optionalString (p != "") "mr ${p},\n")
417 + (lib.splitString "\n" etc."ld-nix.so.preload".text)
418 + # TODO: avoid this line splitting by nixifying ld-nix.so.preload as a list or attrset,
419 + # and make services.config.malloc use it.
421 + r ${pkgs.tzdata}/share/zoneinfo/**,
422 + r ${pkgs.stdenv.cc.libc}/share/i18n/**,
424 + "abstractions/bash" = ''
425 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/bash
426 + # system-wide bash configuration
427 + ${etcRule "profile.dos"}
428 + ${etcRule "profile"}
429 + ${etcRule "profile.d"}
430 + ${etcRule {path="profile.d"; trail="/*";}}
431 + ${etcRule "bashrc"}
432 + ${etcRule "bash.bashrc"}
433 + ${etcRule "bash.bashrc.local"}
434 + ${etcRule "bash_completion"}
435 + ${etcRule "bash_completion.d"}
436 + ${etcRule {path="bash_completion.d"; trail="/*";}}
437 + # bash relies on system-wide readline configuration
438 + ${etcRule "inputrc"}
439 + # bash inspects filesystems at startup
440 + # and /etc/mtab is linked to /proc/mounts
443 + # run out of /etc/bash.bashrc
444 + ${etcRule "DIR_COLORS"}
446 + "abstractions/cups-client" = ''
447 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/cpus-client
448 + ${etcRule "cups/cups-client.conf"}
450 + "abstractions/consoles" = ''
451 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/consoles
453 + "abstractions/dbus-session-strict" = ''
454 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/dbus-session-strict
455 + ${etcRule "machine-id"}
457 + "abstractions/dconf" = ''
458 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/dconf
459 + ${etcRule {path="dconf"; trail="/**";}}
461 + "abstractions/dri-common" = ''
462 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/dri-common
465 + # The config.fonts.fontconfig NixOS module adds many files to /etc/fonts/
466 + # by symlinking them but without exporting them outside of its NixOS module,
467 + # those are therefore added there to this "abstractions/fonts".
468 + "abstractions/fonts" = ''
469 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/fonts
470 + ${etcRule {path="fonts"; trail="/**";}}
472 + "abstractions/gnome" = ''
473 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/gnome
474 + ${etcRule {path="gnome"; trail="/gtkrc*";}}
475 + ${etcRule {path="gtk"; trail="/*";}}
476 + ${etcRule {path="gtk-2.0"; trail="/*";}}
477 + ${etcRule {path="gtk-3.0"; trail="/*";}}
478 + ${etcRule "orbitrc"}
479 + #include <abstractions/fonts>
480 + ${etcRule {path="pango"; trail="/*";}}
481 + ${etcRule {path="/etc/gnome-vfs-2.0"; trail="/modules/";}}
482 + ${etcRule {path="/etc/gnome-vfs-2.0"; trail="/modules/*";}}
483 + ${etcRule "papersize"}
484 + ${etcRule {path="cups"; trail="/lpoptions";}}
485 + ${etcRule {path="gnome"; trail="/defaults.list";}}
486 + ${etcRule {path="xdg"; trail="/{,*-}mimeapps.list";}}
487 + ${etcRule "xdg/mimeapps.list"}
489 + "abstractions/kde" = ''
490 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/kde
491 + ${etcRule {path="qt3"; trail="/kstylerc";}}
492 + ${etcRule {path="qt3"; trail="/qt_plugins_3.3rc";}}
493 + ${etcRule {path="qt3"; trail="/qtrc";}}
495 + ${etcRule {path="kde3"; trail="/*";}}
496 + ${etcRule "kde4rc"}
497 + ${etcRule {path="xdg"; trail="/kdeglobals";}}
498 + ${etcRule {path="xdg"; trail="/Trolltech.conf";}}
500 + "abstractions/kerberosclient" = ''
501 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/kerberosclient
502 + ${etcRule {path="krb5.keytab"; mode="rk";}}
503 + ${etcRule "krb5.conf"}
504 + ${etcRule "krb5.conf.d"}
505 + ${etcRule {path="krb5.conf.d"; trail="/*";}}
507 + # config files found via strings on libs
508 + ${etcRule "krb.conf"}
509 + ${etcRule "krb.realms"}
510 + ${etcRule "srvtab"}
512 + "abstractions/ldapclient" = ''
513 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/ldapclient
514 + ${etcRule "ldap.conf"}
515 + ${etcRule "ldap.secret"}
516 + ${etcRule {path="openldap"; trail="/*";}}
517 + ${etcRule {path="openldap"; trail="/cacerts/*";}}
518 + ${etcRule {path="sasl2"; trail="/*";}}
520 + "abstractions/likewise" = ''
521 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/likewise
523 + "abstractions/mdns" = ''
524 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/mdns
525 + ${etcRule "nss_mdns.conf"}
527 + "abstractions/nameservice" = ''
528 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nameservice
530 + # Many programs wish to perform nameservice-like operations, such as
531 + # looking up users by name or id, groups by name or id, hosts by name
532 + # or IP, etc. These operations may be performed through files, dns,
533 + # NIS, NIS+, LDAP, hesiod, wins, etc. Allow them all here.
535 + ${etcRule "host.conf"}
537 + ${etcRule "nsswitch.conf"}
538 + ${etcRule "gai.conf"}
539 + ${etcRule "passwd"}
540 + ${etcRule "protocols"}
542 + # libtirpc (used for NIS/YP login) needs this
543 + ${etcRule "netconfig"}
545 + ${etcRule "resolv.conf"}
547 + ${etcRule {path="samba"; trail="/lmhosts";}}
548 + ${etcRule "services"}
550 + ${etcRule "default/nss"}
552 + # libnl-3-200 via libnss-gw-name
553 + ${etcRule {path="libnl"; trail="/classid";}}
554 + ${etcRule {path="libnl-3"; trail="/classid";}}
556 + mr ${getLib pkgs.nss}/lib/libnss_*.so*,
557 + mr ${getLib pkgs.nss}/lib64/libnss_*.so*,
559 + "abstractions/nis" = ''
560 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nis
562 + "abstractions/nvidia" = ''
563 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nvidia
564 + ${etcRule "vdpau_wrapper.cfg"}
566 + "abstractions/opencl-common" = ''
567 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/opencl-common
568 + ${etcRule {path="OpenCL"; trail="/**";}}
570 + "abstractions/opencl-mesa" = ''
571 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/opencl-mesa
572 + ${etcRule "default/drirc"}
574 + "abstractions/openssl" = ''
575 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/openssl
576 + ${etcRule {path="ssl"; trail="/openssl.cnf";}}
578 + "abstractions/p11-kit" = ''
579 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/p11-kit
580 + ${etcRule {path="pkcs11"; trail="/";}}
581 + ${etcRule {path="pkcs11"; trail="/pkcs11.conf";}}
582 + ${etcRule {path="pkcs11"; trail="/modules/";}}
583 + ${etcRule {path="pkcs11"; trail="/modules/*";}}
585 + "abstractions/perl" = ''
586 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/perl
587 + ${etcRule {path="perl"; trail="/**";}}
589 + "abstractions/php" = ''
590 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/php
591 + ${etcRule {path="php"; trail="/**/";}}
592 + ${etcRule {path="php5"; trail="/**/";}}
593 + ${etcRule {path="php7"; trail="/**/";}}
594 + ${etcRule {path="php"; trail="/**.ini";}}
595 + ${etcRule {path="php5"; trail="/**.ini";}}
596 + ${etcRule {path="php7"; trail="/**.ini";}}
598 + "abstractions/postfix-common" = ''
599 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/postfix-common
600 + ${etcRule "mailname"}
601 + ${etcRule {path="postfix"; trail="/*.cf";}}
602 + ${etcRule "postfix/main.cf"}
603 + ${etcRule "postfix/master.cf"}
605 + "abstractions/python" = ''
606 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/python
607 + ${etcRule {path="python2.4"; trail="/**";}}
608 + ${etcRule {path="python2.5"; trail="/**";}}
609 + ${etcRule {path="python2.6"; trail="/**";}}
610 + ${etcRule {path="python2.7"; trail="/**";}}
611 + ${etcRule {path="python3.0"; trail="/**";}}
612 + ${etcRule {path="python3.1"; trail="/**";}}
613 + ${etcRule {path="python3.2"; trail="/**";}}
614 + ${etcRule {path="python3.3"; trail="/**";}}
615 + ${etcRule {path="python3.4"; trail="/**";}}
616 + ${etcRule {path="python3.5"; trail="/**";}}
617 + ${etcRule {path="python3.6"; trail="/**";}}
618 + ${etcRule {path="python3.7"; trail="/**";}}
619 + ${etcRule {path="python3.8"; trail="/**";}}
620 + ${etcRule {path="python3.9"; trail="/**";}}
622 + "abstractions/qt5" = ''
623 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/qt5
624 + ${etcRule {path="xdg"; trail="/QtProject/qtlogging.ini";}}
625 + ${etcRule {path="xdg/QtProject"; trail="/qtlogging.ini";}}
626 + ${etcRule "xdg/QtProject/qtlogging.ini"}
628 + "abstractions/samba" = ''
629 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/samba
630 + ${etcRule {path="samba"; trail="/*";}}
632 + "abstractions/ssl_certs" = ''
633 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/ssl_certs
634 + ${etcRule "ssl/certs/ca-certificates.crt"}
635 + ${etcRule "ssl/certs/ca-bundle.crt"}
636 + ${etcRule "pki/tls/certs/ca-bundle.crt"}
638 + ${etcRule {path="ssl/trust"; trail="/";}}
639 + ${etcRule {path="ssl/trust"; trail="/*";}}
640 + ${etcRule {path="ssl/trust/anchors"; trail="/";}}
641 + ${etcRule {path="ssl/trust/anchors"; trail="/**";}}
642 + ${etcRule {path="pki/trust"; trail="/";}}
643 + ${etcRule {path="pki/trust"; trail="/*";}}
644 + ${etcRule {path="pki/trust/anchors"; trail="/";}}
645 + ${etcRule {path="pki/trust/anchors"; trail="/**";}}
647 + # security.acme NixOS module
648 + r /var/lib/acme/*/cert.pem,
649 + r /var/lib/acme/*/chain.pem,
650 + r /var/lib/acme/*/fullchain.pem,
652 + "abstractions/ssl_keys" = ''
653 + # security.acme NixOS module
654 + r /var/lib/acme/*/full.pem,
655 + r /var/lib/acme/*/key.pem,
657 + "abstractions/vulkan" = ''
658 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/vulkan
659 + ${etcRule {path="vulkan/icd.d"; trail="/";}}
660 + ${etcRule {path="vulkan/icd.d"; trail="/*.json";}}
662 + "abstractions/winbind" = ''
663 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/winbind
664 + ${etcRule {path="samba"; trail="/smb.conf";}}
665 + ${etcRule {path="samba"; trail="/dhcp.conf";}}
667 + "abstractions/X" = ''
668 + #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/X
669 + ${etcRule {path="X11/cursors"; trail="/";}}
670 + ${etcRule {path="X11/cursors"; trail="/**";}}
674 diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
675 index 688344852ae..339dbd4cb3a 100644
676 --- a/nixos/modules/security/pam.nix
677 +++ b/nixos/modules/security/pam.nix
678 @@ -789,6 +789,57 @@ in
679 runuser-l = { rootOK = true; unixAuth = false; };
682 + security.apparmor.includes."abstractions/pam" = let
683 + isEnabled = test: fold or false (map test (attrValues config.security.pam.services));
685 + ${optionalString use_ldap
686 + "mr ${pam_ldap}/lib/security/pam_ldap.so,"}
687 + ${optionalString config.services.sssd.enable
688 + "mr ${pkgs.sssd}/lib/security/pam_sss.so,"}
689 + ${optionalString config.krb5.enable ''
690 + mr ${pam_krb5}/lib/security/pam_krb5.so,
691 + mr ${pam_ccreds}/lib/security/pam_ccreds.so,
693 + ${optionalString (isEnabled (cfg: cfg.googleOsLoginAccountVerification)) ''
694 + mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so,
695 + mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_admin.so,
697 + ${optionalString (isEnabled (cfg: cfg.googleOsLoginAuthentication))
698 + "mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so,"}
699 + ${optionalString (config.security.pam.enableSSHAgentAuth && isEnabled (cfg: cfg.sshAgentAuth))
700 + "mr ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so,"}
701 + ${optionalString (isEnabled (cfg: cfg.fprintAuth))
702 + "mr ${pkgs.fprintd}/lib/security/pam_fprintd.so,"}
703 + ${optionalString (isEnabled (cfg: cfg.u2fAuth))
704 + "mr ${pkgs.pam_u2f}/lib/security/pam_u2f.so,"}
705 + ${optionalString (isEnabled (cfg: cfg.usbAuth))
706 + "mr ${pkgs.pam_usb}/lib/security/pam_usb.so,"}
707 + ${optionalString (isEnabled (cfg: cfg.oathAuth))
708 + "mr ${pkgs.oathToolkit}/lib/security/pam_oath.so,"}
709 + ${optionalString (isEnabled (cfg: cfg.yubicoAuth))
710 + "mr ${pkgs.yubico-pam}/lib/security/pam_yubico.so,"}
711 + ${optionalString (isEnabled (cfg: cfg.duoSecurity.enable))
712 + "mr ${pkgs.duo-unix}/lib/security/pam_duo.so,"}
713 + ${optionalString (isEnabled (cfg: cfg.otpwAuth))
714 + "mr ${pkgs.otpw}/lib/security/pam_otpw.so,"}
715 + ${optionalString config.security.pam.enableEcryptfs
716 + "mr ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so,"}
717 + ${optionalString (isEnabled (cfg: cfg.pamMount))
718 + "mr ${pkgs.pam_mount}/lib/security/pam_mount.so,"}
719 + ${optionalString config.services.samba.syncPasswordsByPam
720 + "mr ${pkgs.samba}/lib/security/pam_smbpass.so,"}
721 + ${optionalString (isEnabled (cfg: cfg.enableGnomeKeyring))
722 + "mr ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so,"}
723 + ${optionalString (isEnabled (cfg: cfg.startSession))
724 + "mr ${pkgs.systemd}/lib/security/pam_systemd.so,"}
725 + ${optionalString (isEnabled (cfg: cfg.enableAppArmor) && config.security.apparmor.enable)
726 + "mr ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so,"}
727 + ${optionalString (isEnabled (cfg: cfg.enableKwallet))
728 + "mr ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so,"}
729 + ${optionalString config.virtualisation.lxc.lxcfs.enable
730 + "mr ${pkgs.lxc}/lib/security/pam_cgfs.so"}
736 diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
737 index 1bfcf2de82f..a1c6d949bfe 100644
738 --- a/nixos/modules/services/torrent/transmission.nix
739 +++ b/nixos/modules/services/torrent/transmission.nix
741 -{ config, lib, pkgs, ... }:
742 +{ config, lib, pkgs, options, ... }:
747 cfg = config.services.transmission;
748 + inherit (config.environment) etc;
749 apparmor = config.security.apparmor.enable;
751 - homeDir = cfg.home;
752 - downloadDirPermissions = cfg.downloadDirPermissions;
753 - downloadDir = "${homeDir}/Downloads";
754 - incompleteDir = "${homeDir}/.incomplete";
756 - settingsDir = "${homeDir}/config";
757 - settingsFile = pkgs.writeText "settings.json" (builtins.toJSON fullSettings);
759 - # for users in group "transmission" to have access to torrents
760 - fullSettings = { umask = 2; download-dir = downloadDir; incomplete-dir = incompleteDir; } // cfg.settings;
762 - preStart = pkgs.writeScript "transmission-pre-start" ''
763 - #!${pkgs.runtimeShell}
765 - cp -f ${settingsFile} ${settingsDir}/settings.json
767 + stateDir = "/var/lib/transmission";
768 + # TODO: switch to configGen.json once RFC0042 is implemented
769 + settingsFile = pkgs.writeText "settings.json" (builtins.toJSON (cfg.settings // {
770 + download-dir = "${stateDir}/Downloads";
771 + incomplete-dir = "${stateDir}/.incomplete";
773 + settingsDir = ".config/transmission-daemon";
774 + makeAbsolute = base: path:
775 + if builtins.match "^/.*" path == null
776 + then base+"/"+path else path;
780 services.transmission = {
781 - enable = mkOption {
785 - Whether or not to enable the headless Transmission BitTorrent daemon.
786 + enable = mkEnableOption ''the headless Transmission BitTorrent daemon.
788 - Transmission daemon can be controlled via the RPC interface using
789 - transmission-remote or the WebUI (http://localhost:9091/ by default).
790 + Transmission daemon can be controlled via the RPC interface using
791 + transmission-remote, the WebUI (http://${cfg.settings.rpc-bind-address}:${toString cfg.settings.rpc-port}/ by default),
792 + or other clients like stig or tremc.
794 - Torrents are downloaded to ${downloadDir} by default and are
795 - accessible to users in the "transmission" group.
798 + Torrents are downloaded to ${cfg.settings.download-dir} by default and are
799 + accessible to users in the "transmission" group'';
801 - settings = mkOption {
802 + settings = mkOption rec {
803 + # TODO: switch to types.config.json as prescribed by RFC0042 once it's implemented
806 + let super = recursiveUpdate default attrs; in
808 + download-dir = makeAbsolute cfg.home super.download-dir;
809 + incomplete-dir = makeAbsolute cfg.home super.incomplete-dir;
813 - download-dir = downloadDir;
814 - incomplete-dir = incompleteDir;
815 + download-dir = "${cfg.home}/Downloads";
816 + incomplete-dir = "${cfg.home}/.incomplete";
817 incomplete-dir-enabled = true;
819 + peer-port-random-high = 65535;
820 + peer-port-random-low = 49152;
821 + peer-port-random-on-start = false;
822 + rpc-bind-address = "127.0.0.1";
824 + umask = 18; # 0o022 in decimal as expected by Transmission, obtained with: echo $((8#022))
825 + utp-enabled = true;
830 rpc-whitelist = "127.0.0.1,192.168.*.*";
833 - Attribute set whos fields overwrites fields in settings.json (each
834 - time the service starts). String values must be quoted, integer and
835 + Attribute set whose fields overwrites fields in
836 + <literal>.config/transmission-daemon/settings.json</literal>
837 + (each time the service starts). String values must be quoted, integer and
838 boolean values must not.
840 See https://github.com/transmission/transmission/wiki/Editing-Configuration-Files
841 @@ -70,22 +75,30 @@ in
845 - The permissions to set for download-dir and incomplete-dir.
846 - They will be applied on every service start.
847 + The permissions set by the <literal>systemd-tmpfiles-setup</literal> service
848 + on <link linkend="opt-services.transmission.settings">settings.download-dir</link>
849 + and <link linkend="opt-services.transmission.settings">settings.incomplete-dir</link>.
856 - description = "TCP port number to run the RPC/web interface.";
859 + TCP port number to run the RPC/web interface.
861 + If instead you want to change the peer port,
862 + use <link linkend="opt-services.transmission.settings">settings.peer-port</link>
863 + or <link linkend="opt-services.transmission.settings">settings.peer-port-random-on-start</link>.
869 - default = "/var/lib/transmission";
870 + default = stateDir;
872 - The directory where transmission will create files.
873 + The directory where Transmission will create <literal>.config/transmission-daemon/</literal>.
874 + as well as <literal>Downloads/</literal> unless <link linkend="opt-services.transmission.settings">settings.download-dir</link> is changed,
875 + and <literal>.incomplete/</literal> unless <link linkend="opt-services.transmission.settings">settings.incomplete-dir</link> is changed.
879 @@ -100,32 +113,136 @@ in
880 default = "transmission";
881 description = "Group account under which Transmission runs.";
884 + credentialsFile = mkOption {
887 + Path to a JSON file to be merged with the settings.
888 + Useful to merge a file which is better kept out of the Nix store
889 + because it contains sensible data like <link linkend="opt-services.transmission.settings">settings.rpc-password</link>.
891 + default = "/dev/null";
892 + example = "/var/lib/secrets/transmission/settings.json";
895 + openFirewall = mkEnableOption "opening of the peer port(s) in the firewall";
899 config = mkIf cfg.enable {
900 - systemd.tmpfiles.rules = [
901 - "d '${homeDir}' 0770 '${cfg.user}' '${cfg.group}' - -"
902 - "d '${settingsDir}' 0700 '${cfg.user}' '${cfg.group}' - -"
903 - "d '${fullSettings.download-dir}' '${downloadDirPermissions}' '${cfg.user}' '${cfg.group}' - -"
904 - "d '${fullSettings.incomplete-dir}' '${downloadDirPermissions}' '${cfg.user}' '${cfg.group}' - -"
905 + systemd.tmpfiles.rules =
906 + optional (cfg.home != stateDir) "d '${cfg.home}/${settingsDir}' 700 '${cfg.user}' '${cfg.group}' - -"
907 + ++ [ "d '${cfg.settings.download-dir}' '${cfg.downloadDirPermissions}' '${cfg.user}' '${cfg.group}' - -" ]
908 + ++ optional cfg.settings.incomplete-dir-enabled
909 + "d '${cfg.settings.incomplete-dir}' '${cfg.downloadDirPermissions}' '${cfg.user}' '${cfg.group}' - -";
912 + { assertion = builtins.match "^/.*" cfg.home != null;
913 + message = "`services.transmission.home' must be an absolute path.";
915 + { assertion = types.port.check cfg.settings.rpc-port;
916 + message = "${toString cfg.settings.rpc-port} is not a valid port number for `services.transmission.settings.rpc-port`.";
918 + # In case both port and settings.rpc-port are explicitely defined: they must be the same.
919 + { assertion = !options.services.transmission.port.isDefined || cfg.port == cfg.settings.rpc-port;
920 + message = "`services.transmission.port' is not equal to `services.transmission.settings.rpc-port'";
924 + services.transmission.settings =
925 + optionalAttrs options.services.transmission.port.isDefined { rpc-port = cfg.port; };
927 systemd.services.transmission = {
928 description = "Transmission BitTorrent Service";
929 after = [ "network.target" ] ++ optional apparmor "apparmor.service";
930 - requires = mkIf apparmor [ "apparmor.service" ];
931 + requires = optional apparmor "apparmor.service";
932 wantedBy = [ "multi-user.target" ];
933 + environment.CURL_CA_BUNDLE = etc."ssl/certs/ca-certificates.crt".source;
935 - # 1) Only the "transmission" user and group have access to torrents.
936 - # 2) Optionally update/force specific fields into the configuration file.
937 - serviceConfig.ExecStartPre = preStart;
938 - serviceConfig.ExecStart = "${pkgs.transmission}/bin/transmission-daemon -f --port ${toString config.services.transmission.port} --config-dir ${settingsDir}";
939 - serviceConfig.ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
940 - serviceConfig.User = cfg.user;
941 - serviceConfig.Group = cfg.group;
942 - # NOTE: transmission has an internal umask that also must be set (in settings.json)
943 - serviceConfig.UMask = "0002";
945 + WorkingDirectory = stateDir;
946 + # Use "+" because credentialsFile may not be accessible to the User.
947 + ExecStartPre = "+" + pkgs.writeShellScript "transmission-prestart" ''
949 + ${pkgs.jq}/bin/jq --slurp add ${settingsFile} '${cfg.credentialsFile}' |
950 + install -m 700 -o ${cfg.user} -g ${cfg.group} /dev/stdin '${stateDir}/${settingsDir}/settings.json'
952 + ExecStart = "${pkgs.transmission}/bin/transmission-daemon -f";
953 + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
956 + StateDirectory = removePrefix "/var/lib/" stateDir + "/" + settingsDir;
957 + StateDirectoryMode = "0700";
959 + optional (cfg.home != stateDir) "${cfg.home}/${settingsDir}:${stateDir}/${settingsDir}"
960 + ++ [ "${cfg.settings.download-dir}:${stateDir}/Downloads" ]
961 + ++ optional cfg.settings.incomplete-dir-enabled "${cfg.settings.incomplete-dir}:${stateDir}/.incomplete";
962 + # The following options give:
963 + # systemd-analyze security transmission
964 + # → Overall exposure level for transmission.service: 1.5 OK
965 + AmbientCapabilities = "";
966 + CapabilityBoundingSet = "";
967 + LockPersonality = true;
968 + MemoryDenyWriteExecute = true;
969 + NoNewPrivileges = true;
970 + PrivateDevices = true;
971 + PrivateMounts = true;
972 + PrivateNetwork = false;
974 + PrivateUsers = false;
975 + ProtectClock = true;
976 + ProtectControlGroups = true;
977 + ProtectHome = mkDefault true;
978 + ProtectHostname = true;
979 + ProtectKernelLogs = true;
980 + ProtectKernelModules = true;
981 + ProtectKernelTunables = true;
982 + ProtectSystem = mkDefault "strict";
983 + ReadWritePaths = [ stateDir ];
985 + RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
986 + RestrictNamespaces = true;
987 + RestrictRealtime = true;
988 + RestrictSUIDSGID = true;
989 + # In case transmission crashes with status=31/SYS,
990 + # having systemd.coredump.enable = true
991 + # and environment.enableDebugInfo = true
992 + # enables to use coredumpctl debug to find the denied syscall.
993 + SystemCallFilter = [
1012 + # Reached when querying infos through RPC (eg. with stig)
1025 + SystemCallArchitectures = "native";
1030 # It's useful to have transmission in path, e.g. for remote control
1031 @@ -133,32 +250,57 @@ in
1033 users.users = optionalAttrs (cfg.user == "transmission") ({
1035 - name = "transmission";
1037 uid = config.ids.uids.transmission;
1038 description = "Transmission BitTorrent user";
1040 - createHome = true;
1045 users.groups = optionalAttrs (cfg.group == "transmission") ({
1047 - name = "transmission";
1048 gid = config.ids.gids.transmission;
1052 - # AppArmor profile
1053 - security.apparmor.profiles = mkIf apparmor [
1054 - (pkgs.writeText "apparmor-transmission-daemon" ''
1055 + networking.firewall = mkIf cfg.openFirewall (
1056 + if cfg.settings.peer-port-random-on-start
1058 + { allowedTCPPortRanges =
1059 + [ { from = cfg.settings.peer-port-random-low;
1060 + to = cfg.settings.peer-port-random-high;
1063 + allowedUDPPortRanges =
1064 + [ { from = cfg.settings.peer-port-random-low;
1065 + to = cfg.settings.peer-port-random-high;
1070 + { allowedTCPPorts = [ cfg.settings.peer-port ];
1071 + allowedUDPPorts = [ cfg.settings.peer-port ];
1075 + # Transmission uses a single UDP socket in order to implement multiple uTP sockets,
1076 + # and thus expects large kernel buffers for the UDP socket,
1077 + # at least up to the values hardcoded here:
1078 + # https://trac.transmissionbt.com/browser/trunk/libtransmission/tr-udp.c?rev=11956.
1079 + boot.kernel.sysctl = mkIf cfg.settings.utp-enabled {
1080 + "net.core.rmem_max" = mkDefault "4194304"; # 4MB
1081 + "net.core.wmem_max" = mkDefault "1048576"; # 1MB
1084 + security.apparmor.policies."bin/transmission-daemon".profile = ''
1085 #include <tunables/global>
1087 ${pkgs.transmission}/bin/transmission-daemon {
1088 #include <abstractions/base>
1089 #include <abstractions/nameservice>
1091 - ${getLib pkgs.glibc}/lib/*.so mr,
1092 + ${getLib pkgs.stdenv.cc.cc}/lib/*.so* mr,
1093 + ${getLib pkgs.stdenv.cc.libc}/lib/*.so* mr,
1094 ${getLib pkgs.libevent}/lib/libevent*.so* mr,
1095 ${getLib pkgs.curl}/lib/libcurl*.so* mr,
1096 ${getLib pkgs.openssl}/lib/libssl*.so* mr,
1097 @@ -176,27 +318,29 @@ in
1098 ${getLib pkgs.lz4}/lib/liblz4*.so* mr,
1099 ${getLib pkgs.libkrb5}/lib/lib*.so* mr,
1100 ${getLib pkgs.keyutils}/lib/libkeyutils*.so* mr,
1101 - ${getLib pkgs.utillinuxMinimal.out}/lib/libblkid.so.* mr,
1102 - ${getLib pkgs.utillinuxMinimal.out}/lib/libmount.so.* mr,
1103 - ${getLib pkgs.utillinuxMinimal.out}/lib/libuuid.so.* mr,
1104 - ${getLib pkgs.gcc.cc.lib}/lib/libstdc++.so.* mr,
1105 - ${getLib pkgs.gcc.cc.lib}/lib/libgcc_s.so.* mr,
1106 + ${getLib pkgs.utillinuxMinimal.out}/lib/libblkid.so* mr,
1107 + ${getLib pkgs.utillinuxMinimal.out}/lib/libmount.so* mr,
1108 + ${getLib pkgs.utillinuxMinimal.out}/lib/libuuid.so* mr,
1110 @{PROC}/sys/kernel/random/uuid r,
1111 @{PROC}/sys/vm/overcommit_memory r,
1112 + #@{PROC}/@{pid}/environ r,
1113 + @{PROC}/@{pid}/mounts r,
1114 + /tmp/tr_session_id_* rwk,
1116 - ${pkgs.openssl.out}/etc/** r,
1117 + ${pkgs.openssl.out}/etc/** r,
1118 + ${config.systemd.services.transmission.environment.CURL_CA_BUNDLE} r,
1119 ${pkgs.transmission}/share/transmission/** r,
1121 - owner ${settingsDir}/** rw,
1122 + owner ${stateDir}/${settingsDir}/** rw,
1124 - ${fullSettings.download-dir}/** rw,
1125 - ${optionalString fullSettings.incomplete-dir-enabled ''
1126 - ${fullSettings.incomplete-dir}/** rw,
1127 + ${stateDir}/Downloads/** rw,
1128 + ${optionalString cfg.settings.incomplete-dir-enabled ''
1129 + ${stateDir}/.incomplete/** rw,
1137 + meta.maintainers = with lib.maintainers; [ julm ];
1139 diff --git a/nixos/modules/virtualisation/lxc.nix b/nixos/modules/virtualisation/lxc.nix
1140 index f484d5ee59a..a2f4a9867c6 100644
1141 --- a/nixos/modules/virtualisation/lxc.nix
1142 +++ b/nixos/modules/virtualisation/lxc.nix
1143 @@ -74,9 +74,13 @@ in
1144 systemd.tmpfiles.rules = [ "d /var/lib/lxc/rootfs 0755 root root -" ];
1146 security.apparmor.packages = [ pkgs.lxc ];
1147 - security.apparmor.profiles = [
1148 - "${pkgs.lxc}/etc/apparmor.d/lxc-containers"
1149 - "${pkgs.lxc}/etc/apparmor.d/usr.bin.lxc-start"
1151 + security.apparmor.policies = {
1152 + "bin/lxc-start".profile = ''
1153 + #include ${pkgs.lxc}/etc/apparmor.d/usr.bin.lxc-start
1155 + "lxc-containers".profile = ''
1156 + #include ${pkgs.lxc}/etc/apparmor.d/lxc-containers
1161 diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix
1162 index 3958fc2c1d7..84c8e88f8b4 100644
1163 --- a/nixos/modules/virtualisation/lxd.nix
1164 +++ b/nixos/modules/virtualisation/lxd.nix
1165 @@ -93,11 +93,15 @@ in
1167 security.apparmor = {
1170 - "${cfg.lxcPackage}/etc/apparmor.d/usr.bin.lxc-start"
1171 - "${cfg.lxcPackage}/etc/apparmor.d/lxc-containers"
1173 packages = [ cfg.lxcPackage ];
1175 + "bin/lxc-start".profile = ''
1176 + #include ${cfg.lxcPackage}/etc/apparmor.d/usr.bin.lxc-start
1178 + "lxc-containers".profile = ''
1179 + #include ${cfg.lxcPackage}/etc/apparmor.d/lxc-containers
1184 systemd.services.lxd = {
1185 diff --git a/nixos/tests/bittorrent.nix b/nixos/tests/bittorrent.nix
1186 index 0a97d5556a2..c195b60cd56 100644
1187 --- a/nixos/tests/bittorrent.nix
1188 +++ b/nixos/tests/bittorrent.nix
1189 @@ -19,6 +19,7 @@ let
1190 externalClient2Address = "80.100.100.2";
1191 externalTrackerAddress = "80.100.100.3";
1193 + download-dir = "/var/lib/transmission/Downloads";
1194 transmissionConfig = { ... }: {
1195 environment.systemPackages = [ pkgs.transmission ];
1196 services.transmission = {
1197 @@ -26,6 +27,7 @@ let
1199 dht-enabled = false;
1201 + inherit download-dir;
1205 @@ -117,12 +119,12 @@ in
1206 router.wait_for_unit("miniupnpd")
1208 # Create the torrent.
1209 - tracker.succeed("mkdir /tmp/data")
1210 + tracker.succeed("mkdir ${download-dir}/data")
1212 - "cp ${file} /tmp/data/test.tar.bz2"
1213 + "cp ${file} ${download-dir}/data/test.tar.bz2"
1216 - "transmission-create /tmp/data/test.tar.bz2 --private --tracker http://${externalTrackerAddress}:6969/announce --outfile /tmp/test.torrent"
1217 + "transmission-create ${download-dir}/data/test.tar.bz2 --private --tracker http://${externalTrackerAddress}:6969/announce --outfile /tmp/test.torrent"
1219 tracker.succeed("chmod 644 /tmp/test.torrent")
1221 @@ -133,18 +135,16 @@ in
1223 # Start the initial seeder.
1225 - "transmission-remote --add /tmp/test.torrent --no-portmap --no-dht --download-dir /tmp/data"
1226 + "transmission-remote --add /tmp/test.torrent --no-portmap --no-dht --download-dir ${download-dir}/data"
1229 # Now we should be able to download from the client behind the NAT.
1230 tracker.wait_for_unit("httpd")
1231 client1.wait_for_unit("network-online.target")
1232 + client1.succeed("transmission-remote --add http://${externalTrackerAddress}/test.torrent >&2 &")
1233 + client1.wait_for_file("${download-dir}/test.tar.bz2")
1235 - "transmission-remote --add http://${externalTrackerAddress}/test.torrent --download-dir /tmp >&2 &"
1237 - client1.wait_for_file("/tmp/test.tar.bz2")
1239 - "cmp /tmp/test.tar.bz2 ${file}"
1240 + "cmp ${download-dir}/test.tar.bz2 ${file}"
1243 # Bring down the initial seeder.
1244 @@ -154,11 +154,11 @@ in
1245 # the first client created a NAT hole in the router.
1246 client2.wait_for_unit("network-online.target")
1248 - "transmission-remote --add http://${externalTrackerAddress}/test.torrent --no-portmap --no-dht --download-dir /tmp >&2 &"
1249 + "transmission-remote --add http://${externalTrackerAddress}/test.torrent --no-portmap --no-dht >&2 &"
1251 - client2.wait_for_file("/tmp/test.tar.bz2")
1252 + client2.wait_for_file("${download-dir}/test.tar.bz2")
1254 - "cmp /tmp/test.tar.bz2 ${file}"
1255 + "cmp ${download-dir}/test.tar.bz2 ${file}"
1259 diff --git a/pkgs/os-specific/linux/apparmor/default.nix b/pkgs/os-specific/linux/apparmor/default.nix
1260 index 66c2582603c..0205abc290b 100644
1261 --- a/pkgs/os-specific/linux/apparmor/default.nix
1262 +++ b/pkgs/os-specific/linux/apparmor/default.nix
1274 @@ -38,6 +42,22 @@ let
1275 sha256 = "0xw028iqp69j9mxv0kbwraplgkj5i5djdlgf0anpkc5cdbsf96r9";
1278 + aa-teardown = writeShellScript "aa-teardown" ''
1279 + SECURITYFS=/sys/kernel/security
1280 + SFS_MOUNTPOINT="$SECURITYFS/apparmor"
1281 + ${gnused}/bin/sed -e "s/ (\(enforce\|complain\))$//" "$SFS_MOUNTPOINT/profiles" | \
1282 + LC_COLLATE=C ${coreutils}/bin/sort | ${gnugrep}/bin/grep -v // | {
1283 + while read -r profile ; do
1284 + printf "%s" "$profile" > "$SFS_MOUNTPOINT/.remove"
1286 + if [ "$rc" -ne 0 ] ; then
1290 + exit "''${retval:-0}"
1295 substituteInPlace ./common/Make.rules --replace "/usr/bin/pod2man" "${buildPackages.perl}/bin/pod2man"
1296 substituteInPlace ./common/Make.rules --replace "/usr/bin/pod2html" "${buildPackages.perl}/bin/pod2html"
1297 @@ -142,6 +162,8 @@ let
1298 # aa-notify checks its name and does not work named ".aa-notify-wrapped"
1299 mv $out/bin/aa-notify $out/bin/aa-notify-wrapped
1300 makeWrapper ${perl}/bin/perl $out/bin/aa-notify --set PERL5LIB ${libapparmor}/${perl.libPrefix} --add-flags $out/bin/aa-notify-wrapped
1302 + ln -s ${aa-teardown} $out/bin/aa-teardown