+++ /dev/null
-diff --git a/nixos/doc/manual/release-notes/rl-2009.xml b/nixos/doc/manual/release-notes/rl-2009.xml
-index 2225619d481..d5c1a5cb614 100644
---- a/nixos/doc/manual/release-notes/rl-2009.xml
-+++ b/nixos/doc/manual/release-notes/rl-2009.xml
-@@ -680,6 +680,37 @@ systemd.services.nginx.serviceConfig.ReadWritePaths = [ "/var/www" ];
- was removed, as udev gained native support to handle FIDO security tokens.
- </para>
- </listitem>
-+ <listitem>
-+ <para>
-+ The <literal>services.transmission</literal> module
-+ was enhanced with the new options:
-+ <xref linkend="opt-services.transmission.credentialsFile"/>,
-+ <xref linkend="opt-services.transmission.openFirewall"/>,
-+ and <xref linkend="opt-services.transmission.performanceNetParameters"/>.
-+ </para>
-+ <para>
-+ <literal>transmission-daemon</literal> is now started with additional systemd sandbox/hardening options for better security.
-+ Please <link xlink:href="https://github.com/NixOS/nixpkgs/issues">report</link>
-+ any use case where this is not working well.
-+ In particular, the <literal>RootDirectory</literal> option newly set
-+ forbids uploading or downloading a torrent outside of the default directory
-+ configured at <link linkend="opt-services.transmission.settings">settings.download-dir</link>.
-+ If you really need Transmission to access other directories,
-+ you must include those directories into the <literal>BindPaths</literal> of the service:
-+<programlisting>
-+systemd.services.transmission.serviceConfig.BindPaths = [ "/path/to/alternative/download-dir" ];
-+</programlisting>
-+ </para>
-+ <para>
-+ Also, connection to the RPC (Remote Procedure Call) of <literal>transmission-daemon</literal>
-+ is now only available on the local network interface by default.
-+ Use:
-+<programlisting>
-+services.transmission.settings.rpc-bind-address = "0.0.0.0";
-+</programlisting>
-+ to get the previous behavior of listening on all network interfaces.
-+ </para>
-+ </listitem>
- <listitem>
- <para>
- With this release <literal>systemd-networkd</literal> (when enabled through <xref linkend="opt-networking.useNetworkd"/>)
-diff --git a/nixos/modules/config/fonts/fontconfig.nix b/nixos/modules/config/fonts/fontconfig.nix
-index 52d284f739b..3670487200d 100644
---- a/nixos/modules/config/fonts/fontconfig.nix
-+++ b/nixos/modules/config/fonts/fontconfig.nix
-@@ -485,6 +485,38 @@ in
- (mkIf cfg.enable {
- environment.systemPackages = [ pkgs.fontconfig ];
- environment.etc.fonts.source = "${fontconfigEtc}/etc/fonts/";
-+ security.apparmor.includes."abstractions/fonts" = ''
-+ # fonts.conf
-+ r ${supportFontsConf},
-+ r ${latestPkg.out}/etc/fonts/fonts.conf,
-+
-+ # fontconfig default config files
-+ r ${supportPkg.out}/etc/fonts/conf.d/*.conf,
-+ r ${latestPkg.out}/etc/fonts/conf.d/*.conf,
-+
-+ # 00-nixos-cache.conf
-+ r ${cacheConfSupport},
-+ r ${cacheConfLatest},
-+
-+ # 10-nixos-rendering.conf
-+ r ${renderConf},
-+
-+ # local.conf (indirect priority 51)
-+ ${optionalString (cfg.localConf != "") ''
-+ r ${localConf},
-+ ''}
-+
-+ # 52-nixos-default-fonts.conf
-+ r ${defaultFontsConf},
-+
-+ # 53-no-bitmaps.conf
-+ r ${rejectBitmaps},
-+
-+ ${optionalString (!cfg.allowType1) ''
-+ # 53-nixos-reject-type1.conf
-+ r ${rejectType1},
-+ ''}
-+ '';
- })
- (mkIf (cfg.enable && !cfg.penultimate.enable) {
- fonts.fontconfig.confPackages = [ confPkg ];
-diff --git a/nixos/modules/security/apparmor-suid.nix b/nixos/modules/security/apparmor-suid.nix
-index 6c479e070e2..e6d9467f296 100644
---- a/nixos/modules/security/apparmor-suid.nix
-+++ b/nixos/modules/security/apparmor-suid.nix
-@@ -21,9 +21,9 @@ with lib;
- };
-
- config = mkIf (cfg.confineSUIDApplications) {
-- security.apparmor.profiles = [ (pkgs.writeText "ping" ''
-+ security.apparmor.policies."bin/ping".profile = ''
- #include <tunables/global>
-- /run/wrappers/bin/ping {
-+ /run/wrappers/wrappers.*/ping {
- #include <abstractions/base>
- #include <abstractions/consoles>
- #include <abstractions/nameservice>
-@@ -32,10 +32,19 @@ with lib;
- capability setuid,
- network inet raw,
-
-- ${pkgs.stdenv.cc.libc.out}/lib/*.so mr,
-- ${pkgs.libcap.lib}/lib/libcap.so* mr,
-- ${pkgs.attr.out}/lib/libattr.so* mr,
-+ ${getLib pkgs.stdenv.cc.cc}/lib/*.so* mr,
-+ ${getLib pkgs.stdenv.cc.libc}/lib/*.so* mr,
-+ ${getLib pkgs.stdenv.cc.libc}/lib/gconv/gconv-modules r,
-+ ${getLib pkgs.glibcLocales}/lib/locale/locale-archive r,
-+ ${getLib pkgs.attr.out}/lib/libattr.so* mr,
-+ ${getLib pkgs.libcap.lib}/lib/libcap.so* mr,
-+ ${getLib pkgs.libcap_ng}/lib/libcap-ng.so* mr,
-+ ${getLib pkgs.libidn2}/lib/libidn2.so* mr,
-+ ${getLib pkgs.libunistring}/lib/libunistring.so* mr,
-+ ${getLib pkgs.nettle}/lib/libnettle.so* mr,
-
-+ #@{PROC}/@{pid}/environ r,
-+ /run/wrappers/wrappers.*/ping.real r,
- ${pkgs.iputils}/bin/ping mixr,
-
- #/etc/modules.conf r,
-@@ -43,7 +52,7 @@ with lib;
- ## Site-specific additions and overrides. See local/README for details.
- ##include <local/bin.ping>
- }
-- '') ];
-+ '';
- };
-
- }
-diff --git a/nixos/modules/security/apparmor.nix b/nixos/modules/security/apparmor.nix
-index cfc65b347bc..f9abb18afd2 100644
---- a/nixos/modules/security/apparmor.nix
-+++ b/nixos/modules/security/apparmor.nix
-@@ -1,59 +1,190 @@
- { config, lib, pkgs, ... }:
-
- let
-- inherit (lib) mkIf mkOption types concatMapStrings;
-+ inherit (builtins) head match readFile;
-+ inherit (lib) types;
-+ inherit (config.environment) etc;
- cfg = config.security.apparmor;
-+ mkDisableOption = name: lib.mkEnableOption name // {
-+ default = true;
-+ example = false;
-+ };
- in
-
- {
-- options = {
-- security.apparmor = {
-- enable = mkOption {
-- type = types.bool;
-- default = false;
-- description = "Enable the AppArmor Mandatory Access Control system.";
-- };
-- profiles = mkOption {
-- type = types.listOf types.path;
-- default = [];
-- description = "List of files containing AppArmor profiles.";
-- };
-- packages = mkOption {
-- type = types.listOf types.package;
-- default = [];
-- description = "List of packages to be added to apparmor's include path";
-- };
-- };
-- };
-+ imports = [
-+ (lib.mkRemovedOptionModule [ "security" "apparmor" "profiles" ] "Please use the new option: `security.apparmor.policies'.")
-+ apparmor/profiles.nix
-+ ];
-+ options = {
-+ security.apparmor = {
-+ enable = lib.mkEnableOption "the AppArmor Mandatory Access Control system";
-+ policies = lib.mkOption {
-+ description = ''
-+ AppArmor policies.
-+ '';
-+ type = types.attrsOf (types.submodule ({ name, config, ... }: {
-+ options = {
-+ enable = mkDisableOption "loading of the profile into the kernel";
-+ enforce = mkDisableOption "enforcing of the policy or only complain in the logs";
-+ profile = lib.mkOption {
-+ description = "The policy of the profile.";
-+ type = types.lines;
-+ apply = pkgs.writeText name;
-+ };
-+ };
-+ }));
-+ default = {};
-+ };
-+ includes = lib.mkOption {
-+ type = types.attrsOf types.lines;
-+ default = [];
-+ description = ''
-+ List of paths to be added to AppArmor's searched paths
-+ when resolving absolute #include directives.
-+ '';
-+ apply = lib.mapAttrs pkgs.writeText;
-+ };
-+ packages = lib.mkOption {
-+ type = types.listOf types.package;
-+ default = [];
-+ description = "List of packages to be added to AppArmor's include path";
-+ };
-+ enableCache = lib.mkEnableOption ''caching of AppArmor policies
-+ in <literal>/var/cache/apparmor/</literal>.
-+ Beware that AppArmor policies almost always contain Nix store paths,
-+ and thus produce at each change of these paths
-+ a new cached version accumulating in the cache.
-+ '';
-+ killUnconfinedConfinables = mkDisableOption ''killing of processes
-+ which have an AppArmor profile enabled
-+ (in <link linkend="opt-security.apparmor.policies">policies</link>)
-+ but are not confined (because AppArmor can only confine new processes).
-+ '';
-+ };
-+ };
-
-- config = mkIf cfg.enable {
-- environment.systemPackages = [ pkgs.apparmor-utils ];
-+ config = lib.mkIf cfg.enable {
-+ environment.systemPackages = [ pkgs.apparmor-utils ];
-+ environment.etc."apparmor.d".source = pkgs.linkFarm "apparmor.d" (
-+ lib.mapAttrsToList (name: p: {inherit name; path=p.profile;}) cfg.policies ++
-+ lib.mapAttrsToList (name: path: {inherit name path;}) cfg.includes
-+ );
-+ environment.etc."apparmor/parser.conf".text = ''
-+ ${if cfg.enableCache then "write-cache" else "skip-cache"}
-+ cache-loc /var/cache/apparmor
-+ Include /etc/apparmor.d
-+ '' +
-+ lib.concatMapStrings (p: "Include ${p}/etc/apparmor.d\n") cfg.packages;
-+ environment.etc."apparmor/logprof.conf".text = ''
-+ [settings]
-+ profiledir = /etc/apparmor.d
-+ inactive_profiledir = ${pkgs.apparmor-profiles}/share/apparmor/extra-profiles
-+ logfiles = /var/log/audit/audit.log /var/log/syslog /var/log/messages
-
-- boot.kernelParams = [ "apparmor=1" "security=apparmor" ];
-+ parser = ${pkgs.apparmor-parser}/bin/apparmor_parser
-+ ldd = ${pkgs.glibc.bin}/bin/ldd
-+ logger = ${pkgs.utillinux}/bin/logger
-
-- systemd.services.apparmor = let
-- paths = concatMapStrings (s: " -I ${s}/etc/apparmor.d")
-- ([ pkgs.apparmor-profiles ] ++ cfg.packages);
-- in {
-- after = [ "local-fs.target" ];
-- before = [ "sysinit.target" ];
-- wantedBy = [ "multi-user.target" ];
-- unitConfig = {
-- DefaultDependencies = "no";
-- };
-- serviceConfig = {
-- Type = "oneshot";
-- RemainAfterExit = "yes";
-- ExecStart = map (p:
-- ''${pkgs.apparmor-parser}/bin/apparmor_parser -rKv ${paths} "${p}"''
-- ) cfg.profiles;
-- ExecStop = map (p:
-- ''${pkgs.apparmor-parser}/bin/apparmor_parser -Rv "${p}"''
-- ) cfg.profiles;
-- ExecReload = map (p:
-- ''${pkgs.apparmor-parser}/bin/apparmor_parser --reload ${paths} "${p}"''
-- ) cfg.profiles;
-- };
-- };
-- };
-+ # customize how file ownership permissions are presented
-+ # 0 - off
-+ # 1 - default of what ever mode the log reported
-+ # 2 - force the new permissions to be user
-+ # 3 - force all perms on the rule to be user
-+ default_owner_prompt = 1
-+
-+ # custom directory locations to look for #includes
-+ #
-+ # each name should be a valid directory containing possible #include
-+ # candidate files under the profile dir which by default is /etc/apparmor.d.
-+ #
-+ # So an entry of my-includes will allow /etc/apparmor.d/my-includes to
-+ # be used by the yast UI and profiling tools as a source of #include
-+ # files.
-+ custom_includes =
-+
-+ [qualifiers]
-+ ${pkgs.runtimeShell} = icnu
-+ ${pkgs.bashInteractive}/bin/sh = icnu
-+ ${pkgs.bashInteractive}/bin/bash = icnu
-+ '' + head (match "^.*\\[qualifiers](.*)" # Drop the original [settings] section.
-+ (readFile "${pkgs.apparmor-utils}/etc/apparmor/logprof.conf"));
-+
-+ boot.kernelParams = [ "apparmor=1" "security=apparmor" ];
-+
-+ systemd.services.apparmor = {
-+ after = [
-+ "local-fs.target"
-+ "systemd-journald-audit.socket"
-+ ];
-+ before = [ "sysinit.target" ];
-+ wantedBy = [ "multi-user.target" ];
-+ restartTriggers = [
-+ etc."apparmor/parser.conf".source
-+ etc."apparmor.d".source
-+ ];
-+ unitConfig = {
-+ Description="Load AppArmor policies";
-+ DefaultDependencies = "no";
-+ ConditionSecurity = "apparmor";
-+ };
-+ # Reloading instead of restarting enables to load new AppArmor profiles
-+ # without necessarily restarting all services which have Requires=apparmor.service
-+ # It works by:
-+ # - Adding or replacing into the kernel profiles enabled in cfg.policies
-+ # (because AppArmor can do that without stopping the processes already confined).
-+ # - Removing from the kernel any profile whose name is not
-+ # one of the names within the content of the profiles in cfg.policies.
-+ # - Killing the processes which are unconfined but now have a profile loaded
-+ # (because AppArmor can only confine new processes).
-+ reloadIfChanged = true;
-+ # Avoid searchs in /usr/share/locale/
-+ environment.LANG="C";
-+ serviceConfig = let
-+ enabledPolicies = lib.attrValues (lib.filterAttrs (n: p: p.enable) cfg.policies);
-+ removeDisabledProfiles = pkgs.writeShellScript "apparmor-remove" ''
-+ set -eux
-+
-+ enabledProfiles=$(mktemp)
-+ loadedProfiles=$(mktemp)
-+ trap "rm -f $enabledProfiles $loadedProfiles" EXIT
-+
-+ ${pkgs.apparmor-parser}/bin/apparmor_parser --names /dev/null ${
-+ lib.concatMapStrings (p: "\\\n "+p.profile) enabledPolicies} |
-+ sort -u >"$enabledProfiles"
-+
-+ sed -e "s/ (\(enforce\|complain\))$//" /sys/kernel/security/apparmor/profiles |
-+ sort -u >"$loadedProfiles"
-+
-+ comm -23 "$loadedProfiles" "$enabledProfiles" |
-+ while IFS=$'\n\r' read -r profile
-+ do printf %s "$profile" >/sys/kernel/security/apparmor/.remove
-+ done
-+ '';
-+ killUnconfinedConfinables = pkgs.writeShellScript "apparmor-kill" ''
-+ set -eux
-+ ${pkgs.apparmor-utils}/bin/aa-status --json |
-+ ${pkgs.jq}/bin/jq --raw-output '.processes | .[] | .[] | select (.status == "unconfined") | .pid' |
-+ xargs --verbose --no-run-if-empty --delimiter='\n' \
-+ kill
-+ '';
-+ commonOpts = p: "--verbose --show-cache ${lib.optionalString (!p.enforce) "--complain "}${p.profile}";
-+ in {
-+ Type = "oneshot";
-+ RemainAfterExit = "yes";
-+ ExecStartPre = "${pkgs.apparmor-utils}/bin/aa-teardown";
-+ ExecStart = map (p: "${pkgs.apparmor-parser}/bin/apparmor_parser --add ${commonOpts p}") enabledPolicies;
-+ ExecStartPost = lib.optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
-+ ExecReload =
-+ map (p: "${pkgs.apparmor-parser}/bin/apparmor_parser --replace ${commonOpts p}") enabledPolicies ++
-+ [ removeDisabledProfiles ] ++
-+ lib.optional cfg.killUnconfinedConfinables killUnconfinedConfinables;
-+ ExecStop = "${pkgs.apparmor-utils}/bin/aa-teardown";
-+ CacheDirectory = [ "apparmor" ];
-+ CacheDirectoryMode = "0700";
-+ };
-+ };
-+ };
-+
-+ meta.maintainers = with lib.maintainers; [ julm ];
- }
-diff --git a/nixos/modules/security/apparmor/profiles.nix b/nixos/modules/security/apparmor/profiles.nix
-new file mode 100644
-index 00000000000..7e33e630798
---- /dev/null
-+++ b/nixos/modules/security/apparmor/profiles.nix
-@@ -0,0 +1,333 @@
-+{ config, lib, pkgs, ... }:
-+let
-+ inherit (builtins) attrNames hasAttr isAttrs;
-+ inherit (lib) getLib;
-+ inherit (config.environment) etc;
-+ etcRule = arg:
-+ let go = {path ? null, mode ? "r", trail ? ""}:
-+ lib.optionalString (hasAttr path etc)
-+ "${mode} ${config.environment.etc."${path}".source}${trail},";
-+ in if isAttrs arg
-+ then go arg
-+ else go {path=arg;};
-+in
-+{
-+config.security.apparmor.packages = [ pkgs.apparmor-profiles ];
-+# FIXME: most of the etcRule calls below have been
-+# written systematically by converting from apparmor-profiles's profiles
-+# without testing nor deep understanding of their uses,
-+# and thus may need more rules or can have less rules;
-+# this remains to me determined case by case,
-+# some may even be completely useless.
-+config.security.apparmor.includes = {
-+ # This one is included by <tunables/global>
-+ # which is usualy included before any profile.
-+ "abstractions/tunables/alias" = ''
-+ alias /bin -> /run/current-system/sw/bin,
-+ # Unfortunately /etc is mainly built using symlinks,
-+ # thus aliasing does not work.
-+ #alias /etc -> /run/current-system/etc,
-+ alias /lib/modules -> /run/current-system/kernel/lib/modules,
-+ alias /sbin -> /run/current-system/sw/sbin,
-+ alias /usr -> /run/current-system/sw,
-+ '';
-+ "abstractions/audio" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/audio
-+ ${etcRule "asound.conf"}
-+ ${etcRule "esound/esd.conf"}
-+ ${etcRule "libao.conf"}
-+ ${etcRule {path="pulse"; trail="/";}}
-+ ${etcRule {path="pulse"; trail="/**";}}
-+ ${etcRule {path="sound"; trail="/";}}
-+ ${etcRule {path="sound"; trail="/**";}}
-+ ${etcRule {path="alsa/conf.d"; trail="/";}}
-+ ${etcRule {path="alsa/conf.d"; trail="/*";}}
-+ ${etcRule "openal/alsoft.conf"}
-+ ${etcRule "wildmidi/wildmidi.conf"}
-+ '';
-+ # FIXME: security.pam configures more .so than allowed here,
-+ # but has many tests to decide what .so to use,
-+ # so it would be simpler to let security.pam add those .so
-+ # to the present security.apparmor.includes."abstractions/authentication"
-+ "abstractions/authentication" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/authentication
-+ ${etcRule "nologin"}
-+ ${lib.concatMapStringsSep "\n"
-+ (name: "r ${etc."pam.d/${name}".source},")
-+ (attrNames config.security.pam.services)}
-+ mr ${getLib pkgs.pam}/lib/security/pam_filter/*,
-+ mr ${getLib pkgs.pam}/lib/security/pam_*.so,
-+ r ${getLib pkgs.pam}/lib/security/,
-+ ${etcRule "securetty"}
-+ ${etcRule {path="security"; trail="/*";}}
-+ ${etcRule "shadow"}
-+ ${etcRule "gshadow"}
-+ ${etcRule "pwdb.conf"}
-+ ${etcRule "default/passwd"}
-+ ${etcRule "login.defs"}
-+ '';
-+ "abstractions/base" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/base
-+ r ${pkgs.stdenv.cc.libc}/share/locale/**,
-+ r ${pkgs.stdenv.cc.libc}/share/locale.alias,
-+ ${etcRule "localtime"}
-+ r /etc/ld-nix.so.preload,
-+ ${etcRule "ld-nix.so.preload"}
-+ ${lib.concatMapStrings (p: lib.optionalString (p != "") "mr ${p},\n")
-+ (lib.splitString "\n" etc."ld-nix.so.preload".text)
-+ # TODO: avoid this line splitting by nixifying ld-nix.so.preload as a list or attrset,
-+ # and make services.config.malloc use it.
-+ }
-+ r ${pkgs.tzdata}/share/zoneinfo/**,
-+ r ${pkgs.stdenv.cc.libc}/share/i18n/**,
-+ '';
-+ "abstractions/bash" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/bash
-+ # system-wide bash configuration
-+ ${etcRule "profile.dos"}
-+ ${etcRule "profile"}
-+ ${etcRule "profile.d"}
-+ ${etcRule {path="profile.d"; trail="/*";}}
-+ ${etcRule "bashrc"}
-+ ${etcRule "bash.bashrc"}
-+ ${etcRule "bash.bashrc.local"}
-+ ${etcRule "bash_completion"}
-+ ${etcRule "bash_completion.d"}
-+ ${etcRule {path="bash_completion.d"; trail="/*";}}
-+ # bash relies on system-wide readline configuration
-+ ${etcRule "inputrc"}
-+ # bash inspects filesystems at startup
-+ # and /etc/mtab is linked to /proc/mounts
-+ @{PROC}/mounts
-+
-+ # run out of /etc/bash.bashrc
-+ ${etcRule "DIR_COLORS"}
-+ '';
-+ "abstractions/cups-client" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/cpus-client
-+ ${etcRule "cups/cups-client.conf"}
-+ '';
-+ "abstractions/consoles" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/consoles
-+ '';
-+ "abstractions/dbus-session-strict" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/dbus-session-strict
-+ ${etcRule "machine-id"}
-+ '';
-+ "abstractions/dconf" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/dconf
-+ ${etcRule {path="dconf"; trail="/**";}}
-+ '';
-+ "abstractions/dri-common" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/dri-common
-+ ${etcRule "drirc"}
-+ '';
-+ # The config.fonts.fontconfig NixOS module adds many files to /etc/fonts/
-+ # by symlinking them but without exporting them outside of its NixOS module,
-+ # those are therefore added there to this "abstractions/fonts".
-+ "abstractions/fonts" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/fonts
-+ ${etcRule {path="fonts"; trail="/**";}}
-+ '';
-+ "abstractions/gnome" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/gnome
-+ ${etcRule {path="gnome"; trail="/gtkrc*";}}
-+ ${etcRule {path="gtk"; trail="/*";}}
-+ ${etcRule {path="gtk-2.0"; trail="/*";}}
-+ ${etcRule {path="gtk-3.0"; trail="/*";}}
-+ ${etcRule "orbitrc"}
-+ #include <abstractions/fonts>
-+ ${etcRule {path="pango"; trail="/*";}}
-+ ${etcRule {path="/etc/gnome-vfs-2.0"; trail="/modules/";}}
-+ ${etcRule {path="/etc/gnome-vfs-2.0"; trail="/modules/*";}}
-+ ${etcRule "papersize"}
-+ ${etcRule {path="cups"; trail="/lpoptions";}}
-+ ${etcRule {path="gnome"; trail="/defaults.list";}}
-+ ${etcRule {path="xdg"; trail="/{,*-}mimeapps.list";}}
-+ ${etcRule "xdg/mimeapps.list"}
-+ '';
-+ "abstractions/kde" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/kde
-+ ${etcRule {path="qt3"; trail="/kstylerc";}}
-+ ${etcRule {path="qt3"; trail="/qt_plugins_3.3rc";}}
-+ ${etcRule {path="qt3"; trail="/qtrc";}}
-+ ${etcRule "kderc"}
-+ ${etcRule {path="kde3"; trail="/*";}}
-+ ${etcRule "kde4rc"}
-+ ${etcRule {path="xdg"; trail="/kdeglobals";}}
-+ ${etcRule {path="xdg"; trail="/Trolltech.conf";}}
-+ '';
-+ "abstractions/kerberosclient" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/kerberosclient
-+ ${etcRule {path="krb5.keytab"; mode="rk";}}
-+ ${etcRule "krb5.conf"}
-+ ${etcRule "krb5.conf.d"}
-+ ${etcRule {path="krb5.conf.d"; trail="/*";}}
-+
-+ # config files found via strings on libs
-+ ${etcRule "krb.conf"}
-+ ${etcRule "krb.realms"}
-+ ${etcRule "srvtab"}
-+ '';
-+ "abstractions/ldapclient" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/ldapclient
-+ ${etcRule "ldap.conf"}
-+ ${etcRule "ldap.secret"}
-+ ${etcRule {path="openldap"; trail="/*";}}
-+ ${etcRule {path="openldap"; trail="/cacerts/*";}}
-+ ${etcRule {path="sasl2"; trail="/*";}}
-+ '';
-+ "abstractions/likewise" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/likewise
-+ '';
-+ "abstractions/mdns" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/mdns
-+ ${etcRule "nss_mdns.conf"}
-+ '';
-+ "abstractions/nameservice" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nameservice
-+
-+ # Many programs wish to perform nameservice-like operations, such as
-+ # looking up users by name or id, groups by name or id, hosts by name
-+ # or IP, etc. These operations may be performed through files, dns,
-+ # NIS, NIS+, LDAP, hesiod, wins, etc. Allow them all here.
-+ ${etcRule "group"}
-+ ${etcRule "host.conf"}
-+ ${etcRule "hosts"}
-+ ${etcRule "nsswitch.conf"}
-+ ${etcRule "gai.conf"}
-+ ${etcRule "passwd"}
-+ ${etcRule "protocols"}
-+
-+ # libtirpc (used for NIS/YP login) needs this
-+ ${etcRule "netconfig"}
-+
-+ ${etcRule "resolv.conf"}
-+
-+ ${etcRule {path="samba"; trail="/lmhosts";}}
-+ ${etcRule "services"}
-+
-+ ${etcRule "default/nss"}
-+
-+ # libnl-3-200 via libnss-gw-name
-+ ${etcRule {path="libnl"; trail="/classid";}}
-+ ${etcRule {path="libnl-3"; trail="/classid";}}
-+
-+ mr ${getLib pkgs.nss}/lib/libnss_*.so*,
-+ mr ${getLib pkgs.nss}/lib64/libnss_*.so*,
-+ '';
-+ "abstractions/nis" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nis
-+ '';
-+ "abstractions/nvidia" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nvidia
-+ ${etcRule "vdpau_wrapper.cfg"}
-+ '';
-+ "abstractions/opencl-common" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/opencl-common
-+ ${etcRule {path="OpenCL"; trail="/**";}}
-+ '';
-+ "abstractions/opencl-mesa" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/opencl-mesa
-+ ${etcRule "default/drirc"}
-+ '';
-+ "abstractions/openssl" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/openssl
-+ ${etcRule {path="ssl"; trail="/openssl.cnf";}}
-+ '';
-+ "abstractions/p11-kit" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/p11-kit
-+ ${etcRule {path="pkcs11"; trail="/";}}
-+ ${etcRule {path="pkcs11"; trail="/pkcs11.conf";}}
-+ ${etcRule {path="pkcs11"; trail="/modules/";}}
-+ ${etcRule {path="pkcs11"; trail="/modules/*";}}
-+ '';
-+ "abstractions/perl" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/perl
-+ ${etcRule {path="perl"; trail="/**";}}
-+ '';
-+ "abstractions/php" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/php
-+ ${etcRule {path="php"; trail="/**/";}}
-+ ${etcRule {path="php5"; trail="/**/";}}
-+ ${etcRule {path="php7"; trail="/**/";}}
-+ ${etcRule {path="php"; trail="/**.ini";}}
-+ ${etcRule {path="php5"; trail="/**.ini";}}
-+ ${etcRule {path="php7"; trail="/**.ini";}}
-+ '';
-+ "abstractions/postfix-common" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/postfix-common
-+ ${etcRule "mailname"}
-+ ${etcRule {path="postfix"; trail="/*.cf";}}
-+ ${etcRule "postfix/main.cf"}
-+ ${etcRule "postfix/master.cf"}
-+ '';
-+ "abstractions/python" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/python
-+ ${etcRule {path="python2.4"; trail="/**";}}
-+ ${etcRule {path="python2.5"; trail="/**";}}
-+ ${etcRule {path="python2.6"; trail="/**";}}
-+ ${etcRule {path="python2.7"; trail="/**";}}
-+ ${etcRule {path="python3.0"; trail="/**";}}
-+ ${etcRule {path="python3.1"; trail="/**";}}
-+ ${etcRule {path="python3.2"; trail="/**";}}
-+ ${etcRule {path="python3.3"; trail="/**";}}
-+ ${etcRule {path="python3.4"; trail="/**";}}
-+ ${etcRule {path="python3.5"; trail="/**";}}
-+ ${etcRule {path="python3.6"; trail="/**";}}
-+ ${etcRule {path="python3.7"; trail="/**";}}
-+ ${etcRule {path="python3.8"; trail="/**";}}
-+ ${etcRule {path="python3.9"; trail="/**";}}
-+ '';
-+ "abstractions/qt5" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/qt5
-+ ${etcRule {path="xdg"; trail="/QtProject/qtlogging.ini";}}
-+ ${etcRule {path="xdg/QtProject"; trail="/qtlogging.ini";}}
-+ ${etcRule "xdg/QtProject/qtlogging.ini"}
-+ '';
-+ "abstractions/samba" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/samba
-+ ${etcRule {path="samba"; trail="/*";}}
-+ '';
-+ "abstractions/ssl_certs" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/ssl_certs
-+ ${etcRule "ssl/certs/ca-certificates.crt"}
-+ ${etcRule "ssl/certs/ca-bundle.crt"}
-+ ${etcRule "pki/tls/certs/ca-bundle.crt"}
-+
-+ ${etcRule {path="ssl/trust"; trail="/";}}
-+ ${etcRule {path="ssl/trust"; trail="/*";}}
-+ ${etcRule {path="ssl/trust/anchors"; trail="/";}}
-+ ${etcRule {path="ssl/trust/anchors"; trail="/**";}}
-+ ${etcRule {path="pki/trust"; trail="/";}}
-+ ${etcRule {path="pki/trust"; trail="/*";}}
-+ ${etcRule {path="pki/trust/anchors"; trail="/";}}
-+ ${etcRule {path="pki/trust/anchors"; trail="/**";}}
-+
-+ # security.acme NixOS module
-+ r /var/lib/acme/*/cert.pem,
-+ r /var/lib/acme/*/chain.pem,
-+ r /var/lib/acme/*/fullchain.pem,
-+ '';
-+ "abstractions/ssl_keys" = ''
-+ # security.acme NixOS module
-+ r /var/lib/acme/*/full.pem,
-+ r /var/lib/acme/*/key.pem,
-+ '';
-+ "abstractions/vulkan" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/vulkan
-+ ${etcRule {path="vulkan/icd.d"; trail="/";}}
-+ ${etcRule {path="vulkan/icd.d"; trail="/*.json";}}
-+ '';
-+ "abstractions/winbind" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/winbind
-+ ${etcRule {path="samba"; trail="/smb.conf";}}
-+ ${etcRule {path="samba"; trail="/dhcp.conf";}}
-+ '';
-+ "abstractions/X" = ''
-+ #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/X
-+ ${etcRule {path="X11/cursors"; trail="/";}}
-+ ${etcRule {path="X11/cursors"; trail="/**";}}
-+ '';
-+};
-+}
-diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
-index 565c15dec24..66219d37c7d 100644
---- a/nixos/modules/security/pam.nix
-+++ b/nixos/modules/security/pam.nix
-@@ -836,6 +836,57 @@ in
- runuser-l = { rootOK = true; unixAuth = false; };
- };
-
-+ security.apparmor.includes."abstractions/pam" = let
-+ isEnabled = test: fold or false (map test (attrValues config.security.pam.services));
-+ in ''
-+ ${optionalString use_ldap
-+ "mr ${pam_ldap}/lib/security/pam_ldap.so,"}
-+ ${optionalString config.services.sssd.enable
-+ "mr ${pkgs.sssd}/lib/security/pam_sss.so,"}
-+ ${optionalString config.krb5.enable ''
-+ mr ${pam_krb5}/lib/security/pam_krb5.so,
-+ mr ${pam_ccreds}/lib/security/pam_ccreds.so,
-+ ''}
-+ ${optionalString (isEnabled (cfg: cfg.googleOsLoginAccountVerification)) ''
-+ mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so,
-+ mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_admin.so,
-+ ''}
-+ ${optionalString (isEnabled (cfg: cfg.googleOsLoginAuthentication))
-+ "mr ${pkgs.google-compute-engine-oslogin}/lib/pam_oslogin_login.so,"}
-+ ${optionalString (config.security.pam.enableSSHAgentAuth && isEnabled (cfg: cfg.sshAgentAuth))
-+ "mr ${pkgs.pam_ssh_agent_auth}/libexec/pam_ssh_agent_auth.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.fprintAuth))
-+ "mr ${pkgs.fprintd}/lib/security/pam_fprintd.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.u2fAuth))
-+ "mr ${pkgs.pam_u2f}/lib/security/pam_u2f.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.usbAuth))
-+ "mr ${pkgs.pam_usb}/lib/security/pam_usb.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.oathAuth))
-+ "mr ${pkgs.oathToolkit}/lib/security/pam_oath.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.yubicoAuth))
-+ "mr ${pkgs.yubico-pam}/lib/security/pam_yubico.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.duoSecurity.enable))
-+ "mr ${pkgs.duo-unix}/lib/security/pam_duo.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.otpwAuth))
-+ "mr ${pkgs.otpw}/lib/security/pam_otpw.so,"}
-+ ${optionalString config.security.pam.enableEcryptfs
-+ "mr ${pkgs.ecryptfs}/lib/security/pam_ecryptfs.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.pamMount))
-+ "mr ${pkgs.pam_mount}/lib/security/pam_mount.so,"}
-+ ${optionalString config.services.samba.syncPasswordsByPam
-+ "mr ${pkgs.samba}/lib/security/pam_smbpass.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.enableGnomeKeyring))
-+ "mr ${pkgs.gnome3.gnome-keyring}/lib/security/pam_gnome_keyring.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.startSession))
-+ "mr ${pkgs.systemd}/lib/security/pam_systemd.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.enableAppArmor) && config.security.apparmor.enable)
-+ "mr ${pkgs.apparmor-pam}/lib/security/pam_apparmor.so,"}
-+ ${optionalString (isEnabled (cfg: cfg.enableKwallet))
-+ "mr ${pkgs.plasma5.kwallet-pam}/lib/security/pam_kwallet5.so,"}
-+ ${optionalString config.virtualisation.lxc.lxcfs.enable
-+ "mr ${pkgs.lxc}/lib/security/pam_cgfs.so"}
-+ '';
-+
- };
-
- }
-diff --git a/nixos/modules/services/torrent/transmission.nix b/nixos/modules/services/torrent/transmission.nix
-index 1bfcf2de82f..f589c31ad75 100644
---- a/nixos/modules/services/torrent/transmission.nix
-+++ b/nixos/modules/services/torrent/transmission.nix
-@@ -1,52 +1,51 @@
--{ config, lib, pkgs, ... }:
-+{ config, lib, pkgs, options, ... }:
-
- with lib;
-
- let
- cfg = config.services.transmission;
-+ inherit (config.environment) etc;
- apparmor = config.security.apparmor.enable;
--
-- homeDir = cfg.home;
-- downloadDirPermissions = cfg.downloadDirPermissions;
-- downloadDir = "${homeDir}/Downloads";
-- incompleteDir = "${homeDir}/.incomplete";
--
-- settingsDir = "${homeDir}/config";
-- settingsFile = pkgs.writeText "settings.json" (builtins.toJSON fullSettings);
--
-- # for users in group "transmission" to have access to torrents
-- fullSettings = { umask = 2; download-dir = downloadDir; incomplete-dir = incompleteDir; } // cfg.settings;
--
-- preStart = pkgs.writeScript "transmission-pre-start" ''
-- #!${pkgs.runtimeShell}
-- set -ex
-- cp -f ${settingsFile} ${settingsDir}/settings.json
-- '';
-+ rootDir = "/run/transmission";
-+ homeDir = "/var/lib/transmission";
-+ settingsDir = ".config/transmission-daemon";
-+ downloadsDir = "Downloads";
-+ incompleteDir = ".incomplete";
-+ # TODO: switch to configGen.json once RFC0042 is implemented
-+ settingsFile = pkgs.writeText "settings.json" (builtins.toJSON cfg.settings);
- in
- {
- options = {
- services.transmission = {
-- enable = mkOption {
-- type = types.bool;
-- default = false;
-- description = ''
-- Whether or not to enable the headless Transmission BitTorrent daemon.
-+ enable = mkEnableOption ''the headless Transmission BitTorrent daemon.
-
-- Transmission daemon can be controlled via the RPC interface using
-- transmission-remote or the WebUI (http://localhost:9091/ by default).
-+ Transmission daemon can be controlled via the RPC interface using
-+ transmission-remote, the WebUI (http://127.0.0.1:9091/ by default),
-+ or other clients like stig or tremc.
-
-- Torrents are downloaded to ${downloadDir} by default and are
-- accessible to users in the "transmission" group.
-- '';
-- };
-+ Torrents are downloaded to ${homeDir}/${downloadsDir} by default and are
-+ accessible to users in the "transmission" group'';
-
-- settings = mkOption {
-+ settings = mkOption rec {
-+ # TODO: switch to types.config.json as prescribed by RFC0042 once it's implemented
- type = types.attrs;
-+ apply = recursiveUpdate default;
- default =
- {
-- download-dir = downloadDir;
-- incomplete-dir = incompleteDir;
-+ download-dir = "${cfg.home}/${downloadsDir}";
-+ incomplete-dir = "${cfg.home}/${incompleteDir}";
- incomplete-dir-enabled = true;
-+ message-level = 1;
-+ peer-port = 51413;
-+ peer-port-random-high = 65535;
-+ peer-port-random-low = 49152;
-+ peer-port-random-on-start = false;
-+ rpc-bind-address = "127.0.0.1";
-+ rpc-port = 9091;
-+ script-torrent-done-enabled = false;
-+ script-torrent-done-filename = "";
-+ umask = 2; # 0o002 in decimal as expected by Transmission
-+ utp-enabled = true;
- };
- example =
- {
-@@ -56,11 +55,12 @@ in
- rpc-whitelist = "127.0.0.1,192.168.*.*";
- };
- description = ''
-- Attribute set whos fields overwrites fields in settings.json (each
-- time the service starts). String values must be quoted, integer and
-+ Attribute set whose fields overwrites fields in
-+ <literal>.config/transmission-daemon/settings.json</literal>
-+ (each time the service starts). String values must be quoted, integer and
- boolean values must not.
-
-- See https://github.com/transmission/transmission/wiki/Editing-Configuration-Files
-+ See <link xlink:href="https://github.com/transmission/transmission/wiki/Editing-Configuration-Files">Transmission's Wiki</link>
- for documentation.
- '';
- };
-@@ -70,22 +70,32 @@ in
- default = "770";
- example = "775";
- description = ''
-- The permissions to set for download-dir and incomplete-dir.
-- They will be applied on every service start.
-+ The permissions set by <literal>systemd.activationScripts.transmission-daemon</literal>
-+ on the directories <link linkend="opt-services.transmission.settings">settings.download-dir</link>
-+ and <link linkend="opt-services.transmission.settings">settings.incomplete-dir</link>.
-+ Note that you may also want to change
-+ <link linkend="opt-services.transmission.settings">settings.umask</link>.
- '';
- };
-
- port = mkOption {
-- type = types.int;
-- default = 9091;
-- description = "TCP port number to run the RPC/web interface.";
-+ type = types.port;
-+ description = ''
-+ TCP port number to run the RPC/web interface.
-+
-+ If instead you want to change the peer port,
-+ use <link linkend="opt-services.transmission.settings">settings.peer-port</link>
-+ or <link linkend="opt-services.transmission.settings">settings.peer-port-random-on-start</link>.
-+ '';
- };
-
- home = mkOption {
- type = types.path;
-- default = "/var/lib/transmission";
-+ default = homeDir;
- description = ''
-- The directory where transmission will create files.
-+ The directory where Transmission will create <literal>${settingsDir}</literal>.
-+ as well as <literal>${downloadsDir}/</literal> unless <link linkend="opt-services.transmission.settings">settings.download-dir</link> is changed,
-+ and <literal>${incompleteDir}/</literal> unless <link linkend="opt-services.transmission.settings">settings.incomplete-dir</link> is changed.
- '';
- };
-
-@@ -100,32 +110,173 @@ in
- default = "transmission";
- description = "Group account under which Transmission runs.";
- };
-+
-+ credentialsFile = mkOption {
-+ type = types.path;
-+ description = ''
-+ Path to a JSON file to be merged with the settings.
-+ Useful to merge a file which is better kept out of the Nix store
-+ because it contains sensible data like <link linkend="opt-services.transmission.settings">settings.rpc-password</link>.
-+ '';
-+ default = "/dev/null";
-+ example = "/var/lib/secrets/transmission/settings.json";
-+ };
-+
-+ openFirewall = mkEnableOption "opening of the peer port(s) in the firewall";
-+
-+ performanceNetParameters = mkEnableOption ''tweaking of kernel parameters
-+ to open many more connections at the same time.
-+
-+ Note that you may also want to increase
-+ <link linkend="opt-services.transmission.settings">settings.peer-limit-global</link>.
-+ And be aware that these settings are quite aggressive
-+ and might not suite your regular desktop use.
-+ For instance, SSH sessions may time out more easily'';
- };
- };
-
- config = mkIf cfg.enable {
-- systemd.tmpfiles.rules = [
-- "d '${homeDir}' 0770 '${cfg.user}' '${cfg.group}' - -"
-- "d '${settingsDir}' 0700 '${cfg.user}' '${cfg.group}' - -"
-- "d '${fullSettings.download-dir}' '${downloadDirPermissions}' '${cfg.user}' '${cfg.group}' - -"
-- "d '${fullSettings.incomplete-dir}' '${downloadDirPermissions}' '${cfg.user}' '${cfg.group}' - -"
-+ # Note that using systemd.tmpfiles would not work here
-+ # because it would fail when creating a directory
-+ # with a different owner than its parent directory, by saying:
-+ # Detected unsafe path transition /home/foo → /home/foo/Downloads during canonicalization of /home/foo/Downloads
-+ # when /home/foo is not owned by cfg.user.
-+ # Note also that using an ExecStartPre= wouldn't work either
-+ # because BindPaths= needs these directories before.
-+ system.activationScripts.transmission-daemon = ''
-+ install -D -d -m 700 '${cfg.home}/${settingsDir}'
-+ chown -R '${cfg.user}:${cfg.group}' ${cfg.home}/${settingsDir}
-+ install -D -d -m '${cfg.downloadDirPermissions}' -o '${cfg.user}' -g '${cfg.group}' '${cfg.settings.download-dir}'
-+ '' + optionalString cfg.settings.incomplete-dir-enabled ''
-+ install -D -d -m '${cfg.downloadDirPermissions}' -o '${cfg.user}' -g '${cfg.group}' '${cfg.settings.incomplete-dir}'
-+ '';
-+
-+ assertions = [
-+ { assertion = builtins.match "^/.*" cfg.home != null;
-+ message = "`services.transmission.home' must be an absolute path.";
-+ }
-+ { assertion = types.path.check cfg.settings.download-dir;
-+ message = "`services.transmission.settings.download-dir' must be an absolute path.";
-+ }
-+ { assertion = types.path.check cfg.settings.incomplete-dir;
-+ message = "`services.transmission.settings.incomplete-dir' must be an absolute path.";
-+ }
-+ { assertion = cfg.settings.script-torrent-done-filename == "" || types.path.check cfg.settings.script-torrent-done-filename;
-+ message = "`services.transmission.settings.script-torrent-done-filename' must be an absolute path.";
-+ }
-+ { assertion = types.port.check cfg.settings.rpc-port;
-+ message = "${toString cfg.settings.rpc-port} is not a valid port number for `services.transmission.settings.rpc-port`.";
-+ }
-+ # In case both port and settings.rpc-port are explicitely defined: they must be the same.
-+ { assertion = !options.services.transmission.port.isDefined || cfg.port == cfg.settings.rpc-port;
-+ message = "`services.transmission.port' is not equal to `services.transmission.settings.rpc-port'";
-+ }
- ];
-
-+ services.transmission.settings =
-+ optionalAttrs options.services.transmission.port.isDefined { rpc-port = cfg.port; };
-+
- systemd.services.transmission = {
- description = "Transmission BitTorrent Service";
- after = [ "network.target" ] ++ optional apparmor "apparmor.service";
-- requires = mkIf apparmor [ "apparmor.service" ];
-+ requires = optional apparmor "apparmor.service";
- wantedBy = [ "multi-user.target" ];
-+ environment.CURL_CA_BUNDLE = etc."ssl/certs/ca-certificates.crt".source;
-
-- # 1) Only the "transmission" user and group have access to torrents.
-- # 2) Optionally update/force specific fields into the configuration file.
-- serviceConfig.ExecStartPre = preStart;
-- serviceConfig.ExecStart = "${pkgs.transmission}/bin/transmission-daemon -f --port ${toString config.services.transmission.port} --config-dir ${settingsDir}";
-- serviceConfig.ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-- serviceConfig.User = cfg.user;
-- serviceConfig.Group = cfg.group;
-- # NOTE: transmission has an internal umask that also must be set (in settings.json)
-- serviceConfig.UMask = "0002";
-+ serviceConfig = {
-+ # Use "+" because credentialsFile may not be accessible to User= or Group=.
-+ ExecStartPre = "+" + pkgs.writeShellScript "transmission-prestart" ''
-+ set -eu${lib.optionalString (cfg.settings.message-level >= 3) "x"}
-+ ${pkgs.jq}/bin/jq --slurp add ${settingsFile} '${cfg.credentialsFile}' |
-+ install -D -m 600 -o '${cfg.user}' -g '${cfg.group}' /dev/stdin \
-+ '${cfg.home}/${settingsDir}/settings.json'
-+ '';
-+ ExecStart="${pkgs.transmission}/bin/transmission-daemon -f";
-+ ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
-+ User = cfg.user;
-+ Group = cfg.group;
-+ # Create rootDir in the host's mount namespace.
-+ RuntimeDirectory = [(baseNameOf rootDir)];
-+ RuntimeDirectoryMode = "755";
-+ # Avoid mounting rootDir in the own rootDir of ExecStart='s mount namespace.
-+ InaccessiblePaths = ["-+${rootDir}"];
-+ # This is for BindPaths= and BindReadOnlyPaths=
-+ # to allow traversal of directories they create in RootDirectory=.
-+ UMask = "0066";
-+ # Using a RootDirectory= allows to use the same paths download-dir/incomplete-dir
-+ # (which appear in user's interfaces) without requiring cfg.user
-+ # to have access to their parent directories,
-+ # by using BindPaths=/BindReadOnlyPaths=.
-+ # Note that TemporaryFileSystem= could have been used instead
-+ # but not without adding some BindPaths=/BindReadOnlyPaths=
-+ # that would only be needed for ExecStartPre=,
-+ # because RootDirectoryStartOnly=true would not help.
-+ RootDirectory = rootDir;
-+ RootDirectoryStartOnly = true;
-+ MountAPIVFS = true;
-+ BindPaths =
-+ [ "${cfg.home}/${settingsDir}"
-+ cfg.settings.download-dir
-+ ] ++
-+ optional cfg.settings.incomplete-dir-enabled
-+ cfg.settings.incomplete-dir;
-+ BindReadOnlyPaths = [
-+ # No confinement done of /nix/store here like in systemd-confinement.nix,
-+ # an AppArmor profile is provided to get a confinement based upon paths and rights.
-+ builtins.storeDir
-+ "-/etc/hosts"
-+ "-/etc/ld-nix.so.preload"
-+ "-/etc/localtime"
-+ ] ++
-+ optional (cfg.settings.script-torrent-done-enabled &&
-+ cfg.settings.script-torrent-done-filename != "")
-+ cfg.settings.script-torrent-done-filename;
-+ # The following options are only for optimizing:
-+ # systemd-analyze security transmission
-+ AmbientCapabilities = "";
-+ CapabilityBoundingSet = "";
-+ # ProtectClock= adds DeviceAllow=char-rtc r
-+ DeviceAllow = "";
-+ LockPersonality = true;
-+ MemoryDenyWriteExecute = true;
-+ NoNewPrivileges = true;
-+ PrivateDevices = true;
-+ PrivateMounts = true;
-+ PrivateNetwork = false;
-+ PrivateTmp = true;
-+ PrivateUsers = true;
-+ ProtectClock = true;
-+ ProtectControlGroups = true;
-+ # ProtectHome=true would not allow BindPaths= to work accross /home,
-+ # and ProtectHome=tmpfs would break statfs(),
-+ # preventing transmission-daemon to report the available free space.
-+ # However, RootDirectory= is used, so this is not a security concern
-+ # since there would be nothing in /home but any BindPaths= wanted by the user.
-+ ProtectHome = "read-only";
-+ ProtectHostname = true;
-+ ProtectKernelLogs = true;
-+ ProtectKernelModules = true;
-+ ProtectKernelTunables = true;
-+ ProtectSystem = "strict";
-+ RemoveIPC = true;
-+ # AF_UNIX may become usable one day:
-+ # https://github.com/transmission/transmission/issues/441
-+ RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
-+ RestrictNamespaces = true;
-+ RestrictRealtime = true;
-+ RestrictSUIDSGID = true;
-+ SystemCallFilter = [
-+ "@system-service"
-+ # Groups in @system-service which do not contain a syscall
-+ # listed by perf -e 'syscalls:sys_enter_*' transmission-daemon -f
-+ # in tests, and seem likely not necessary for transmission-daemon.
-+ "~@aio" "~@chown" "~@keyring" "~@memlock" "~@resources" "~@setuid" "~@timer"
-+ # In the @privileged group, but reached when querying infos through RPC (eg. with stig).
-+ "quotactl"
-+ ];
-+ SystemCallArchitectures = "native";
-+ SystemCallErrorNumber = "EPERM";
-+ };
- };
-
- # It's useful to have transmission in path, e.g. for remote control
-@@ -133,70 +284,137 @@ in
-
- users.users = optionalAttrs (cfg.user == "transmission") ({
- transmission = {
-- name = "transmission";
- group = cfg.group;
- uid = config.ids.uids.transmission;
- description = "Transmission BitTorrent user";
-- home = homeDir;
-- createHome = true;
-+ home = cfg.home;
- };
- });
-
- users.groups = optionalAttrs (cfg.group == "transmission") ({
- transmission = {
-- name = "transmission";
- gid = config.ids.gids.transmission;
- };
- });
-
-- # AppArmor profile
-- security.apparmor.profiles = mkIf apparmor [
-- (pkgs.writeText "apparmor-transmission-daemon" ''
-- #include <tunables/global>
-+ networking.firewall = mkIf cfg.openFirewall (
-+ if cfg.settings.peer-port-random-on-start
-+ then
-+ { allowedTCPPortRanges =
-+ [ { from = cfg.settings.peer-port-random-low;
-+ to = cfg.settings.peer-port-random-high;
-+ }
-+ ];
-+ allowedUDPPortRanges =
-+ [ { from = cfg.settings.peer-port-random-low;
-+ to = cfg.settings.peer-port-random-high;
-+ }
-+ ];
-+ }
-+ else
-+ { allowedTCPPorts = [ cfg.settings.peer-port ];
-+ allowedUDPPorts = [ cfg.settings.peer-port ];
-+ }
-+ );
-+
-+ boot.kernel.sysctl = mkMerge [
-+ # Transmission uses a single UDP socket in order to implement multiple uTP sockets,
-+ # and thus expects large kernel buffers for the UDP socket,
-+ # https://trac.transmissionbt.com/browser/trunk/libtransmission/tr-udp.c?rev=11956.
-+ # at least up to the values hardcoded here:
-+ (mkIf cfg.settings.utp-enabled {
-+ "net.core.rmem_max" = mkDefault "4194304"; # 4MB
-+ "net.core.wmem_max" = mkDefault "1048576"; # 1MB
-+ })
-+ (mkIf cfg.performanceNetParameters {
-+ # Increase the number of available source (local) TCP and UDP ports to 49151.
-+ # Usual default is 32768 60999, ie. 28231 ports.
-+ # Find out your current usage with: ss -s
-+ "net.ipv4.ip_local_port_range" = "16384 65535";
-+ # Timeout faster generic TCP states.
-+ # Usual default is 600.
-+ # Find out your current usage with: watch -n 1 netstat -nptuo
-+ "net.netfilter.nf_conntrack_generic_timeout" = 60;
-+ # Timeout faster established but inactive connections.
-+ # Usual default is 432000.
-+ "net.netfilter.nf_conntrack_tcp_timeout_established" = 600;
-+ # Clear immediately TCP states after timeout.
-+ # Usual default is 120.
-+ "net.netfilter.nf_conntrack_tcp_timeout_time_wait" = 1;
-+ # Increase the number of trackable connections.
-+ # Usual default is 262144.
-+ # Find out your current usage with: conntrack -C
-+ "net.netfilter.nf_conntrack_max" = 1048576;
-+ })
-+ ];
-+
-+ security.apparmor.policies."bin/transmission-daemon".profile = ''
-+ include <tunables/global>
-
- ${pkgs.transmission}/bin/transmission-daemon {
-- #include <abstractions/base>
-- #include <abstractions/nameservice>
-+ include <abstractions/base>
-+ include <abstractions/nameservice>
-
-- ${getLib pkgs.glibc}/lib/*.so mr,
-- ${getLib pkgs.libevent}/lib/libevent*.so* mr,
-- ${getLib pkgs.curl}/lib/libcurl*.so* mr,
-- ${getLib pkgs.openssl}/lib/libssl*.so* mr,
-- ${getLib pkgs.openssl}/lib/libcrypto*.so* mr,
-- ${getLib pkgs.zlib}/lib/libz*.so* mr,
-- ${getLib pkgs.libssh2}/lib/libssh2*.so* mr,
-- ${getLib pkgs.systemd}/lib/libsystemd*.so* mr,
-- ${getLib pkgs.xz}/lib/liblzma*.so* mr,
-- ${getLib pkgs.libgcrypt}/lib/libgcrypt*.so* mr,
-- ${getLib pkgs.libgpgerror}/lib/libgpg-error*.so* mr,
-- ${getLib pkgs.nghttp2}/lib/libnghttp2*.so* mr,
-- ${getLib pkgs.c-ares}/lib/libcares*.so* mr,
-- ${getLib pkgs.libcap}/lib/libcap*.so* mr,
-- ${getLib pkgs.attr}/lib/libattr*.so* mr,
-- ${getLib pkgs.lz4}/lib/liblz4*.so* mr,
-- ${getLib pkgs.libkrb5}/lib/lib*.so* mr,
-- ${getLib pkgs.keyutils}/lib/libkeyutils*.so* mr,
-- ${getLib pkgs.utillinuxMinimal.out}/lib/libblkid.so.* mr,
-- ${getLib pkgs.utillinuxMinimal.out}/lib/libmount.so.* mr,
-- ${getLib pkgs.utillinuxMinimal.out}/lib/libuuid.so.* mr,
-- ${getLib pkgs.gcc.cc.lib}/lib/libstdc++.so.* mr,
-- ${getLib pkgs.gcc.cc.lib}/lib/libgcc_s.so.* mr,
-+ mr ${getLib pkgs.stdenv.cc.cc}/lib/*.so*,
-+ mr ${getLib pkgs.stdenv.cc.libc}/lib/*.so*,
-+ mr ${getLib pkgs.attr}/lib/libattr*.so*,
-+ mr ${getLib pkgs.c-ares}/lib/libcares*.so*,
-+ mr ${getLib pkgs.curl}/lib/libcurl*.so*,
-+ mr ${getLib pkgs.keyutils}/lib/libkeyutils*.so*,
-+ mr ${getLib pkgs.libcap}/lib/libcap*.so*,
-+ mr ${getLib pkgs.libevent}/lib/libevent*.so*,
-+ mr ${getLib pkgs.libgcrypt}/lib/libgcrypt*.so*,
-+ mr ${getLib pkgs.libgpgerror}/lib/libgpg-error*.so*,
-+ mr ${getLib pkgs.libkrb5}/lib/lib*.so*,
-+ mr ${getLib pkgs.libssh2}/lib/libssh2*.so*,
-+ mr ${getLib pkgs.lz4}/lib/liblz4*.so*,
-+ mr ${getLib pkgs.nghttp2}/lib/libnghttp2*.so*,
-+ mr ${getLib pkgs.openssl}/lib/libcrypto*.so*,
-+ mr ${getLib pkgs.openssl}/lib/libssl*.so*,
-+ mr ${getLib pkgs.systemd}/lib/libsystemd*.so*,
-+ mr ${getLib pkgs.utillinuxMinimal.out}/lib/libblkid.so*,
-+ mr ${getLib pkgs.utillinuxMinimal.out}/lib/libmount.so*,
-+ mr ${getLib pkgs.utillinuxMinimal.out}/lib/libuuid.so*,
-+ mr ${getLib pkgs.xz}/lib/liblzma*.so*,
-+ mr ${getLib pkgs.zlib}/lib/libz*.so*,
-
-- @{PROC}/sys/kernel/random/uuid r,
-- @{PROC}/sys/vm/overcommit_memory r,
-+ r @{PROC}/sys/kernel/random/uuid,
-+ r @{PROC}/sys/vm/overcommit_memory,
-+ # @{pid} is not a kernel variable yet but a regexp
-+ #r @{PROC}/@{pid}/environ,
-+ r @{PROC}/@{pid}/mounts,
-+ rwk /tmp/tr_session_id_*,
-
-- ${pkgs.openssl.out}/etc/** r,
-- ${pkgs.transmission}/share/transmission/** r,
-+ r ${pkgs.openssl.out}/etc/**,
-+ r ${config.systemd.services.transmission.environment.CURL_CA_BUNDLE},
-+ r ${pkgs.transmission}/share/transmission/**,
-
-- owner ${settingsDir}/** rw,
--
-- ${fullSettings.download-dir}/** rw,
-- ${optionalString fullSettings.incomplete-dir-enabled ''
-- ${fullSettings.incomplete-dir}/** rw,
-+ owner rw ${cfg.home}/${settingsDir}/**,
-+ rw ${cfg.settings.download-dir}/**,
-+ ${optionalString cfg.settings.incomplete-dir-enabled ''
-+ rw ${cfg.settings.incomplete-dir}/**,
- ''}
-+ profile dirs {
-+ rw ${cfg.settings.download-dir}/**,
-+ ${optionalString cfg.settings.incomplete-dir-enabled ''
-+ rw ${cfg.settings.incomplete-dir}/**,
-+ ''}
-+ }
-+
-+ ${optionalString (cfg.settings.script-torrent-done-enabled &&
-+ cfg.settings.script-torrent-done-filename != "") ''
-+ # Stack transmission_directories profile on top of
-+ # any existing profile for script-torrent-done-filename
-+ # FIXME: to be tested as I'm not sure it works well with NoNewPrivileges=
-+ # https://gitlab.com/apparmor/apparmor/-/wikis/AppArmorStacking#seccomp-and-no_new_privs
-+ px ${cfg.settings.script-torrent-done-filename} -> &@{dirs},
-+ ''}
-+
-+ # FIXME: enable customizing using https://github.com/NixOS/nixpkgs/pull/93457
-+ # include <local/transmission-daemon>
- }
-- '')
-- ];
-+ '';
- };
-
-+ meta.maintainers = with lib.maintainers; [ julm ];
- }
-diff --git a/nixos/modules/virtualisation/lxc.nix b/nixos/modules/virtualisation/lxc.nix
-index f484d5ee59a..a2f4a9867c6 100644
---- a/nixos/modules/virtualisation/lxc.nix
-+++ b/nixos/modules/virtualisation/lxc.nix
-@@ -74,9 +74,13 @@ in
- systemd.tmpfiles.rules = [ "d /var/lib/lxc/rootfs 0755 root root -" ];
-
- security.apparmor.packages = [ pkgs.lxc ];
-- security.apparmor.profiles = [
-- "${pkgs.lxc}/etc/apparmor.d/lxc-containers"
-- "${pkgs.lxc}/etc/apparmor.d/usr.bin.lxc-start"
-- ];
-+ security.apparmor.policies = {
-+ "bin/lxc-start".profile = ''
-+ #include ${pkgs.lxc}/etc/apparmor.d/usr.bin.lxc-start
-+ '';
-+ "lxc-containers".profile = ''
-+ #include ${pkgs.lxc}/etc/apparmor.d/lxc-containers
-+ '';
-+ };
- };
- }
-diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix
-index 3958fc2c1d7..84c8e88f8b4 100644
---- a/nixos/modules/virtualisation/lxd.nix
-+++ b/nixos/modules/virtualisation/lxd.nix
-@@ -93,11 +93,15 @@ in
-
- security.apparmor = {
- enable = true;
-- profiles = [
-- "${cfg.lxcPackage}/etc/apparmor.d/usr.bin.lxc-start"
-- "${cfg.lxcPackage}/etc/apparmor.d/lxc-containers"
-- ];
- packages = [ cfg.lxcPackage ];
-+ policies = {
-+ "bin/lxc-start".profile = ''
-+ #include ${cfg.lxcPackage}/etc/apparmor.d/usr.bin.lxc-start
-+ '';
-+ "lxc-containers".profile = ''
-+ #include ${cfg.lxcPackage}/etc/apparmor.d/lxc-containers
-+ '';
-+ };
- };
-
- systemd.services.lxd = {
-diff --git a/nixos/tests/bittorrent.nix b/nixos/tests/bittorrent.nix
-index 0a97d5556a2..c195b60cd56 100644
---- a/nixos/tests/bittorrent.nix
-+++ b/nixos/tests/bittorrent.nix
-@@ -19,6 +19,7 @@ let
- externalClient2Address = "80.100.100.2";
- externalTrackerAddress = "80.100.100.3";
-
-+ download-dir = "/var/lib/transmission/Downloads";
- transmissionConfig = { ... }: {
- environment.systemPackages = [ pkgs.transmission ];
- services.transmission = {
-@@ -26,6 +27,7 @@ let
- settings = {
- dht-enabled = false;
- message-level = 3;
-+ inherit download-dir;
- };
- };
- };
-@@ -117,12 +119,12 @@ in
- router.wait_for_unit("miniupnpd")
-
- # Create the torrent.
-- tracker.succeed("mkdir /tmp/data")
-+ tracker.succeed("mkdir ${download-dir}/data")
- tracker.succeed(
-- "cp ${file} /tmp/data/test.tar.bz2"
-+ "cp ${file} ${download-dir}/data/test.tar.bz2"
- )
- tracker.succeed(
-- "transmission-create /tmp/data/test.tar.bz2 --private --tracker http://${externalTrackerAddress}:6969/announce --outfile /tmp/test.torrent"
-+ "transmission-create ${download-dir}/data/test.tar.bz2 --private --tracker http://${externalTrackerAddress}:6969/announce --outfile /tmp/test.torrent"
- )
- tracker.succeed("chmod 644 /tmp/test.torrent")
-
-@@ -133,18 +135,16 @@ in
-
- # Start the initial seeder.
- tracker.succeed(
-- "transmission-remote --add /tmp/test.torrent --no-portmap --no-dht --download-dir /tmp/data"
-+ "transmission-remote --add /tmp/test.torrent --no-portmap --no-dht --download-dir ${download-dir}/data"
- )
-
- # Now we should be able to download from the client behind the NAT.
- tracker.wait_for_unit("httpd")
- client1.wait_for_unit("network-online.target")
-+ client1.succeed("transmission-remote --add http://${externalTrackerAddress}/test.torrent >&2 &")
-+ client1.wait_for_file("${download-dir}/test.tar.bz2")
- client1.succeed(
-- "transmission-remote --add http://${externalTrackerAddress}/test.torrent --download-dir /tmp >&2 &"
-- )
-- client1.wait_for_file("/tmp/test.tar.bz2")
-- client1.succeed(
-- "cmp /tmp/test.tar.bz2 ${file}"
-+ "cmp ${download-dir}/test.tar.bz2 ${file}"
- )
-
- # Bring down the initial seeder.
-@@ -154,11 +154,11 @@ in
- # the first client created a NAT hole in the router.
- client2.wait_for_unit("network-online.target")
- client2.succeed(
-- "transmission-remote --add http://${externalTrackerAddress}/test.torrent --no-portmap --no-dht --download-dir /tmp >&2 &"
-+ "transmission-remote --add http://${externalTrackerAddress}/test.torrent --no-portmap --no-dht >&2 &"
- )
-- client2.wait_for_file("/tmp/test.tar.bz2")
-+ client2.wait_for_file("${download-dir}/test.tar.bz2")
- client2.succeed(
-- "cmp /tmp/test.tar.bz2 ${file}"
-+ "cmp ${download-dir}/test.tar.bz2 ${file}"
- )
- '';
- })
-diff --git a/pkgs/os-specific/linux/apparmor/default.nix b/pkgs/os-specific/linux/apparmor/default.nix
-index 807ab4fa44b..fa7cded9aed 100644
---- a/pkgs/os-specific/linux/apparmor/default.nix
-+++ b/pkgs/os-specific/linux/apparmor/default.nix
-@@ -10,6 +10,10 @@
- , pam
- , libnotify
- , buildPackages
-+, coreutils
-+, gnugrep
-+, gnused
-+, writeShellScript
- }:
-
- let
-@@ -38,6 +42,22 @@ let
- sha256 = "0xw028iqp69j9mxv0kbwraplgkj5i5djdlgf0anpkc5cdbsf96r9";
- };
-
-+ aa-teardown = writeShellScript "aa-teardown" ''
-+ SECURITYFS=/sys/kernel/security
-+ SFS_MOUNTPOINT="$SECURITYFS/apparmor"
-+ ${gnused}/bin/sed -e "s/ (\(enforce\|complain\))$//" "$SFS_MOUNTPOINT/profiles" | \
-+ LC_COLLATE=C ${coreutils}/bin/sort | ${gnugrep}/bin/grep -v // | {
-+ while read -r profile ; do
-+ printf "%s" "$profile" > "$SFS_MOUNTPOINT/.remove"
-+ rc=$?
-+ if [ "$rc" -ne 0 ] ; then
-+ retval=$rc
-+ fi
-+ done
-+ exit "''${retval:-0}"
-+ }
-+ '';
-+
- prePatchCommon = ''
- patch -p1 < ${gnumake43Patch}
- chmod a+x ./common/list_capabilities.sh ./common/list_af_names.sh
-@@ -145,6 +165,8 @@ let
- # aa-notify checks its name and does not work named ".aa-notify-wrapped"
- mv $out/bin/aa-notify $out/bin/aa-notify-wrapped
- makeWrapper ${perl}/bin/perl $out/bin/aa-notify --set PERL5LIB ${libapparmor}/${perl.libPrefix} --add-flags $out/bin/aa-notify-wrapped
-+
-+ ln -s ${aa-teardown} $out/bin/aa-teardown
- '';
-
- inherit doCheck;