apparmor: fix/rewrite security.apparmor
authorJulien Moutinho <julm@sourcephile.fr>
Thu, 16 Jul 2020 04:24:09 +0000 (06:24 +0200)
committerJulien Moutinho <julm@sourcephile.fr>
Thu, 16 Jul 2020 04:24:09 +0000 (06:24 +0200)
nixos/modules.nix
nixos/modules/security/apparmor-suid.nix [new file with mode: 0644]
nixos/modules/security/apparmor.nix [new file with mode: 0644]
nixos/modules/security/apparmor/fix-profiles.patch [new file with mode: 0644]
nixos/modules/services/torrent/transmission.nix
nixos/modules/virtualisation/lxc.nix [new file with mode: 0644]
nixos/modules/virtualisation/lxd.nix [new file with mode: 0644]
servers/losurdo/transmission.nix
shell.nix

index 855f8c933019b0c0b9b6e2b5a802d5b1a0e8dc31..02586217c9b8a71252deca630fa758fd35535d1c 100644 (file)
@@ -11,10 +11,18 @@ imports = [
   modules/services/mail/public-inbox.nix
   #modules/services/mail/mlmmj.nix
   modules/services/torrent/transmission.nix
+  modules/security/apparmor.nix
+  modules/security/apparmor-suid.nix
+  modules/virtualisation/lxc.nix
+  modules/virtualisation/lxd.nix
 ];
 disabledModules = [
   "services/mail/public-inbox.nix"
   "services/mail/mlmmj.nix"
   "services/torrent/transmission.nix"
+  "security/apparmor.nix"
+  "security/apparmor-suid.nix"
+  "virtualisation/lxc.nix"
+  "virtualisation/lxd.nix"
 ];
 }
diff --git a/nixos/modules/security/apparmor-suid.nix b/nixos/modules/security/apparmor-suid.nix
new file mode 100644 (file)
index 0000000..16da043
--- /dev/null
@@ -0,0 +1,50 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.security.apparmor;
+in
+with lib;
+{
+  imports = [
+    (mkRenamedOptionModule [ "security" "virtualization" "flushL1DataCache" ] [ "security" "virtualisation" "flushL1DataCache" ])
+  ];
+
+  options.security.apparmor.confineSUIDApplications = mkOption {
+    type = types.bool;
+    default = true;
+    description = ''
+      Install AppArmor profiles for commonly-used SUID application
+      to mitigate potential privilege escalation attacks due to bugs
+      in such applications.
+
+      Currently available profiles: ping
+    '';
+  };
+
+  config = mkIf (cfg.confineSUIDApplications) {
+    security.apparmor.complainProfiles = [ "bin/ping" ];
+    security.apparmor.profiles."bin/ping" = ''
+      #include <tunables/global>
+      /run/wrappers/bin/ping {
+        #include <abstractions/base>
+        #include <abstractions/consoles>
+        #include <abstractions/nameservice>
+
+        capability net_raw,
+        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,
+
+        ${pkgs.iputils}/bin/ping mixr,
+
+        #/etc/modules.conf r,
+
+        ## 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
new file mode 100644 (file)
index 0000000..787e1d1
--- /dev/null
@@ -0,0 +1,161 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (builtins) attrNames head match readFile;
+  inherit (lib) types;
+  inherit (config.environment) etc;
+  cfg = config.security.apparmor;
+in
+
+{
+  options = {
+    security.apparmor = {
+      enable = lib.mkEnableOption "Enable the AppArmor Mandatory Access Control system.";
+      profiles = lib.mkOption {
+        type = types.attrsOf types.lines;
+        default = {};
+        description = ''
+          Available AppArmor profiles.
+        '';
+        apply = lib.mapAttrs (name: text: pkgs.writeText "${name}" text);
+      };
+      enforceProfiles = lib.mkOption {
+        type = (types.listOf (types.enum (attrNames cfg.profiles))) // {
+          description = "list of profiles";
+        };
+        default = [];
+        description = "List of AppArmor profiles to be enforced.";
+      };
+      complainProfiles = lib.mkOption {
+        type = (types.listOf (types.enum (attrNames cfg.profiles))) // {
+          description = "list of profiles";
+        };
+        default = [];
+        description = "List of AppArmor profiles to be complained.";
+      };
+      includes = lib.mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = ''
+          List of paths to be added to AppArmor's searched paths
+          when resolving absolute #include directives.
+        '';
+      };
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.apparmor-utils ];
+    environment.etc."apparmor.d".source = pkgs.linkFarm "apparmor.d" (
+      lib.mapAttrsToList (name: path: {inherit name path;}) cfg.profiles
+    );
+    environment.etc."apparmor/parser.conf".text =
+      lib.concatMapStringsSep "\n" (p: "#Include ${p}") cfg.includes;
+    environment.etc."apparmor/logprof.conf".text = ''
+      [settings]
+        profiledir = /etc/apparmor.d /etc/subdomain.d
+        inactive_profiledir = ${pkgs.apparmor-profiles}/share/apparmor/extra-profiles
+        logfiles = /var/log/audit/audit.log /var/log/syslog /var/log/messages
+
+        parser = ${pkgs.apparmor-parser}/bin/apparmor_parser ${pkgs.apparmor-parser}/bin/subdomain_parser
+        ldd = ${pkgs.glibc.bin}/bin/ldd
+        logger = ${pkgs.utillinux}/bin/logger
+
+        # 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
+    '' + head (match "^.*\\[qualifiers](.*)" (readFile "${pkgs.apparmor-utils}/etc/apparmor/logprof.conf"));
+    security.apparmor.profiles = {
+      "abstractions/tunables/alias" = ''
+        alias /bin -> /run/current-system/sw/bin,
+        #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/base" = ''
+         #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/base
+         ${etc."hosts".source} r,
+         /etc/ld-nix.so.preload r,
+         ${etc."ld-nix.so.preload".source} r,
+         ${lib.concatMapStrings (p: lib.optionalString (p != "") (p+" mr,\n"))
+           (lib.splitString "\n" etc."ld-nix.so.preload".text)}
+         ${pkgs.tzdata}/share/zoneinfo/** r,
+      '';
+      "abstractions/consoles" = ''
+         #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/consoles
+      '';
+      "abstractions/ldapclient" = ''
+         #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/ldapclient
+      '';
+      "abstractions/kerberosclient" = ''
+         #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/kerberosclient
+      '';
+      "abstractions/likewise" = ''
+         #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/likewise
+      '';
+      "abstractions/mdns" = ''
+         #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/mdns
+      '';
+      "abstractions/nameservice" = ''
+         #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nameservice
+      '';
+      "abstractions/nis" = ''
+         #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/nis
+      '';
+      "abstractions/ssl_certs" = ''
+         #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/ssl_certs
+         ${etc."ssl/certs/ca-certificates.crt".source} r,
+         ${etc."ssl/certs/ca-bundle.crt".source} r,
+      '';
+      "abstractions/winbind" = ''
+         #include ${pkgs.apparmor-profiles}/etc/apparmor.d/abstractions/winbind
+      '';
+    };
+    security.apparmor.includes = [ (pkgs.apparmor-profiles+"/etc/apparmor.d/") ];
+
+    boot.kernelParams = [ "apparmor=1" "security=apparmor" ];
+
+    systemd.services.apparmor = {
+      after = [ "local-fs.target" ];
+      before = [ "sysinit.target" ];
+      wantedBy = [ "multi-user.target" ];
+      unitConfig = {
+        DefaultDependencies = "no";
+      };
+      serviceConfig =
+        let includes = lib.concatMapStringsSep " " (p: "-I ${p}") cfg.includes;
+        in {
+        Type = "oneshot";
+        RemainAfterExit = "yes";
+        ExecStart =
+          map (p: ''${pkgs.apparmor-parser}/bin/apparmor_parser -rKv ${includes} "${cfg.profiles."${p}"}"'') cfg.enforceProfiles ++
+          map (p: ''${pkgs.apparmor-parser}/bin/apparmor_parser -rKvC ${includes} "${cfg.profiles."${p}"}"'') cfg.complainProfiles;
+        ExecStop =
+          map (p: ''${pkgs.apparmor-parser}/bin/apparmor_parser -Rv ${includes} "${cfg.profiles."${p}"}"'') cfg.enforceProfiles ++
+          map (p: ''${pkgs.apparmor-parser}/bin/apparmor_parser -RvC ${includes} "${cfg.profiles."${p}"}"'') cfg.complainProfiles;
+        ExecReload =
+          map (p: ''${pkgs.apparmor-parser}/bin/apparmor_parser --reload ${includes} "${cfg.profiles."${p}"}"'') cfg.enforceProfiles ++
+          map (p: ''${pkgs.apparmor-parser}/bin/apparmor_parser --reload -C ${includes} "${cfg.profiles."${p}"}"'') cfg.complainProfiles;
+      };
+    };
+  };
+
+  meta.maintainers = with lib.maintainers; [ julm ];
+}
diff --git a/nixos/modules/security/apparmor/fix-profiles.patch b/nixos/modules/security/apparmor/fix-profiles.patch
new file mode 100644 (file)
index 0000000..549b605
--- /dev/null
@@ -0,0 +1,63 @@
+diff --git a/etc/apparmor.d/abstractions/base b/etc/apparmor.d/abstractions/base
+index fabb427..2103c3c 100644
+--- a/etc/apparmor.d/abstractions/base
++++ b/etc/apparmor.d/abstractions/base
+@@ -30,13 +30,6 @@
+   /etc/locale/**                 r,
+   /etc/locale.alias              r,
+   /etc/localtime                 r,
+-  /usr/share/locale-bundle/**    r,
+-  /usr/share/locale-langpack/**  r,
+-  /usr/share/locale/**           r,
+-  /usr/share/**/locale/**        r,
+-  /usr/share/zoneinfo/           r,
+-  /usr/share/zoneinfo/**         r,
+-  /usr/share/X11/locale/**       r,
+   /run/systemd/journal/dev-log w,
+   # systemd native journal API (see sd_journal_print(4))
+   /run/systemd/journal/socket w,
+@@ -45,12 +38,6 @@
+   # anything when reading so this is ok.
+   /run/systemd/journal/stdout rw,
+-  /usr/lib{,32,64}/locale/**             mr,
+-  /usr/lib{,32,64}/gconv/*.so            mr,
+-  /usr/lib{,32,64}/gconv/gconv-modules*  mr,
+-  /usr/lib/@{multiarch}/gconv/*.so           mr,
+-  /usr/lib/@{multiarch}/gconv/gconv-modules* mr,
+-
+   # used by glibc when binding to ephemeral ports
+   /etc/bindresvport.blacklist    r,
+@@ -59,20 +46,7 @@
+   /etc/ld.so.cache               mr,
+   /etc/ld.so.conf                r,
+   /etc/ld.so.conf.d/{,*.conf}    r,
+-  /etc/ld.so.preload             r,
+-  /{usr/,}lib{,32,64}/ld{,32,64}-*.so   mr,
+-  /{usr/,}lib/@{multiarch}/ld{,32,64}-*.so    mr,
+-  /{usr/,}lib/tls/i686/{cmov,nosegneg}/ld-*.so     mr,
+-  /{usr/,}lib/i386-linux-gnu/tls/i686/{cmov,nosegneg}/ld-*.so     mr,
+-  /opt/*-linux-uclibc/lib/ld-uClibc*so* mr,
+-
+-  # we might as well allow everything to use common libraries
+-  /{usr/,}lib{,32,64}/**                r,
+-  /{usr/,}lib{,32,64}/**.so*       mr,
+-  /{usr/,}lib/@{multiarch}/**            r,
+-  /{usr/,}lib/@{multiarch}/**.so*   mr,
+-  /{usr/,}lib/tls/i686/{cmov,nosegneg}/*.so*    mr,
+-  /{usr/,}lib/i386-linux-gnu/tls/i686/{cmov,nosegneg}/*.so*    mr,
++  /etc/ld-nix.so.preload         r,
+   # /dev/null is pretty harmless and frequently used
+   /dev/null                      rw,
+@@ -101,9 +75,6 @@
+   # libgcrypt reads some flags from /proc
+   @{PROC}/sys/crypto/*           r,
+-  # some applications will display license information
+-  /usr/share/common-licenses/**  r,
+-
+   # glibc statvfs
+   @{PROC}/filesystems            r,
index 4d16f67554aa2b306bf6d101eae5e65159a0345d..92d64c731c29d871cc7adda62cc998a0a3e61c5b 100644 (file)
@@ -289,69 +289,56 @@ in
         }
     );
 
-    # You can add --Complain to apparmor_parser calls in services.apparmor's ExecStart=
-    # (because aa-complain is not working with the setup currently made by services.apparmor)
-    # then use journalctl -b --grep apparmor= to see denied accesses.
-    security.apparmor.profiles = mkIf apparmor [
-      (pkgs.writeText "apparmor-transmission-daemon" ''
-        #include <tunables/global>
-
-        ${pkgs.transmission}/bin/transmission-daemon {
-          #include <abstractions/base>
-          #include <abstractions/nameservice>
-
-          # FIXME: these lines should be removed once <abstractions/base>
-          # has been fixed to fit NixOS.
-          ${etc."hosts".source} r,
-          /etc/ld-nix.so.preload r,
-          ${etc."ld-nix.so.preload".source} r,
-          ${concatMapStrings (p: optionalString (p != "") (p+" mr,\n"))
-            (splitString "\n" config.environment.etc."ld-nix.so.preload".text)}
-          ${etc."ssl/certs/ca-certificates.crt".source} r,
-
-          ${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,
-          ${pkgs.tzdata}/share/zoneinfo/** r,
-
-          @{PROC}/sys/kernel/random/uuid   r,
-          @{PROC}/sys/vm/overcommit_memory r,
-          @{PROC}/@{pid}/environ r,
-          @{PROC}/@{pid}/mounts r,
-          /tmp/tr_session_id_* rwk,
-
-          ${pkgs.openssl.out}/etc/** r,
-          ${pkgs.transmission}/share/transmission/** r,
-
-          owner ${stateDir}/${settingsDir}/** rw,
-
-          ${stateDir}/Downloads/** rw,
-          ${optionalString cfg.settings.incomplete-dir-enabled ''
-            ${stateDir}/.incomplete/** rw,
-          ''}
-        }
-      '')
-    ];
+    security.apparmor.enforceProfiles = optional apparmor "bin/transmission-daemon";
+    security.apparmor.profiles."bin/transmission-daemon" = ''
+      #include <tunables/global>
+
+      ${pkgs.transmission}/bin/transmission-daemon {
+        #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,
+
+        @{PROC}/sys/kernel/random/uuid   r,
+        @{PROC}/sys/vm/overcommit_memory r,
+        @{PROC}/@{pid}/environ r,
+        @{PROC}/@{pid}/mounts r,
+        /tmp/tr_session_id_* rwk,
+
+        ${pkgs.openssl.out}/etc/** r,
+        ${config.systemd.services.transmission.environment.CURL_CA_BUNDLE} r,
+        ${pkgs.transmission}/share/transmission/** r,
+
+        owner ${stateDir}/${settingsDir}/** rw,
+
+        ${stateDir}/Downloads/** rw,
+        ${optionalString cfg.settings.incomplete-dir-enabled ''
+          ${stateDir}/.incomplete/** rw,
+        ''}
+      }
+    '';
   };
 
   meta.maintainers = with lib.maintainers; [ julm ];
diff --git a/nixos/modules/virtualisation/lxc.nix b/nixos/modules/virtualisation/lxc.nix
new file mode 100644 (file)
index 0000000..db1aeab
--- /dev/null
@@ -0,0 +1,88 @@
+# LXC Configuration
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.virtualisation.lxc;
+
+in
+
+{
+  ###### interface
+
+  options.virtualisation.lxc = {
+    enable =
+      mkOption {
+        type = types.bool;
+        default = false;
+        description =
+          ''
+            This enables Linux Containers (LXC), which provides tools
+            for creating and managing system or application containers
+            on Linux.
+          '';
+      };
+
+    systemConfig =
+      mkOption {
+        type = types.lines;
+        default = "";
+        description =
+          ''
+            This is the system-wide LXC config. See
+            <citerefentry><refentrytitle>lxc.system.conf</refentrytitle>
+            <manvolnum>5</manvolnum></citerefentry>.
+          '';
+      };
+
+    defaultConfig =
+      mkOption {
+        type = types.lines;
+        default = "";
+        description =
+          ''
+            Default config (default.conf) for new containers, i.e. for
+            network config. See <citerefentry><refentrytitle>lxc.container.conf
+            </refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+          '';
+      };
+
+    usernetConfig =
+      mkOption {
+        type = types.lines;
+        default = "";
+        description =
+          ''
+            This is the config file for managing unprivileged user network
+            administration access in LXC. See <citerefentry>
+            <refentrytitle>lxc-usernet</refentrytitle><manvolnum>5</manvolnum>
+            </citerefentry>.
+          '';
+      };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.lxc ];
+    environment.etc."lxc/lxc.conf".text = cfg.systemConfig;
+    environment.etc."lxc/lxc-usernet".text = cfg.usernetConfig;
+    environment.etc."lxc/default.conf".text = cfg.defaultConfig;
+    systemd.tmpfiles.rules = [ "d /var/lib/lxc/rootfs 0755 root root -" ];
+
+    security.apparmor = {
+      profiles = {
+        "bin.lxc-start" = ''
+          #include ${pkgs.lxc}/etc/apparmor.d/usr.bin.lxc-start
+        '';
+        "lxc-containers" = ''
+          #include ${pkgs.lxc}/etc/apparmor.d/lxc-containers
+        '';
+      };
+      includes = [ (pkgs.lxc+"/etc/apparmor.d") ];
+    };
+  };
+}
diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix
new file mode 100644 (file)
index 0000000..b01e1a4
--- /dev/null
@@ -0,0 +1,154 @@
+# Systemd services for lxd.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.virtualisation.lxd;
+  zfsCfg = config.boot.zfs;
+
+in
+
+{
+  ###### interface
+
+  options = {
+    virtualisation.lxd = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          This option enables lxd, a daemon that manages
+          containers. Users in the "lxd" group can interact with
+          the daemon (e.g. to start or stop containers) using the
+          <command>lxc</command> command line tool, among others.
+
+          Most of the time, you'll also want to start lxcfs, so
+          that containers can "see" the limits:
+          <code>
+            virtualisation.lxc.lxcfs.enable = true;
+          </code>
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.lxd.override { nftablesSupport = config.networking.nftables.enable; };
+        defaultText = "pkgs.lxd";
+        description = ''
+          The LXD package to use.
+        '';
+      };
+
+      lxcPackage = mkOption {
+        type = types.package;
+        default = pkgs.lxc;
+        defaultText = "pkgs.lxc";
+        description = ''
+          The LXC package to use with LXD (required for AppArmor profiles).
+        '';
+      };
+
+      zfsPackage = mkOption {
+        type = types.package;
+        default = with pkgs; if zfsCfg.enableUnstable then zfsUnstable else zfs;
+        defaultText = "pkgs.zfs";
+        description = ''
+          The ZFS package to use with LXD.
+        '';
+      };
+
+      zfsSupport = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enables lxd to use zfs as a storage for containers.
+
+          This option is enabled by default if a zfs pool is configured
+          with nixos.
+        '';
+      };
+
+      recommendedSysctlSettings = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          enables various settings to avoid common pitfalls when
+          running containers requiring many file operations.
+          Fixes errors like "Too many open files" or
+          "neighbour: ndisc_cache: neighbor table overflow!".
+          See https://lxd.readthedocs.io/en/latest/production-setup/
+          for details.
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+
+    security.apparmor = {
+      enable = true;
+      profiles = {
+        "bin.lxc-start" = ''
+          #include ${cfg.lxcPackage}/etc/apparmor.d/usr.bin.lxc-start
+        '';
+        "lxc-containers" = ''
+          #include ${cfg.lxcPackage}/etc/apparmor.d/lxc-containers
+        '';
+      };
+      includes = [ (cfg.lxcPackage+"/etc/apparmor.d") ];
+    };
+
+    systemd.services.lxd = {
+      description = "LXD Container Management Daemon";
+
+      wantedBy = [ "multi-user.target" ];
+      after = [ "systemd-udev-settle.service" ];
+
+      path = lib.optional cfg.zfsSupport cfg.zfsPackage;
+
+      preStart = ''
+        mkdir -m 0755 -p /var/lib/lxc/rootfs
+      '';
+
+      serviceConfig = {
+        ExecStart = "@${cfg.package}/bin/lxd lxd --group lxd";
+        Type = "simple";
+        KillMode = "process"; # when stopping, leave the containers alone
+        LimitMEMLOCK = "infinity";
+        LimitNOFILE = "1048576";
+        LimitNPROC = "infinity";
+        TasksMax = "infinity";
+
+        # By default, `lxd` loads configuration files from hard-coded
+        # `/usr/share/lxc/config` - since this is a no-go for us, we have to
+        # explicitly tell it where the actual configuration files are
+        Environment = mkIf (config.virtualisation.lxc.lxcfs.enable)
+          "LXD_LXC_TEMPLATE_CONFIG=${pkgs.lxcfs}/share/lxc/config";
+      };
+    };
+
+    users.groups.lxd.gid = config.ids.gids.lxd;
+
+    users.users.root = {
+      subUidRanges = [ { startUid = 1000000; count = 65536; } ];
+      subGidRanges = [ { startGid = 1000000; count = 65536; } ];
+    };
+
+    boot.kernel.sysctl = mkIf cfg.recommendedSysctlSettings {
+      "fs.inotify.max_queued_events" = 1048576;
+      "fs.inotify.max_user_instances" = 1048576;
+      "fs.inotify.max_user_watches" = 1048576;
+      "vm.max_map_count" = 262144;
+      "kernel.dmesg_restrict" = 1;
+      "net.ipv4.neigh.default.gc_thresh3" = 8192;
+      "net.ipv6.neigh.default.gc_thresh3" = 8192;
+      "kernel.keys.maxkeys" = 2000;
+    };
+  };
+}
index 50843ff8384773d6c817f97252087745fc7d2e68..f3a1636bb01f54fafea3afa853e6ffa17f7f42d1 100644 (file)
@@ -10,11 +10,10 @@ users.groups.transmission.members = [
 networking.nftables.ruleset = ''
   add rule inet filter net2fw tcp dport ${toString transmission.settings.peer-port} counter accept comment "Transmission"
   add rule inet filter net2fw udp dport ${toString transmission.settings.peer-port} counter accept comment "Transmission"
-  add rule inet filter fw2net meta skuid ${toString users.transmission.uid}         counter accept comment "Transmission"
+  add rule inet filter fw2net meta skuid ${transmission.user}                       counter accept comment "Transmission"
 '';
 services.transmission = {
   enable = true;
-  #home = "/var/lib/torrs4";
   settings = {
     message-level = 4;
     download-dir = "Downloads";
@@ -35,15 +34,15 @@ services.transmission = {
     queue-stalled-enabled = true;
     queue-stalled-minutes = 30;
 
-    speed-limit-up = 500;
+    speed-limit-up = 50;
     speed-limit-up-enabled = true;
     alt-speed-enabled = true;
     alt-speed-time-enabled = true;
     alt-speed-down = 0;
-    alt-speed-up = 50;
+    alt-speed-up = 0;
     alt-speed-time-day = 127; # all days. 65; # weekend only
     alt-speed-time-begin = 360; # 06h00 local time
-    alt-speed-time-end = 1380; # 23h00 local time
+    alt-speed-time-end = 1320; # 22h00 local time
     ratio-limit = 3;
     ratio-limit-enabled = true;
 
index e3823f2f2a92f5cc605d6a54032ea97d77882ae3..1091b8f9f8f3432a743e8de59e3aebba7eea19eb 100644 (file)
--- a/shell.nix
+++ b/shell.nix
@@ -23,8 +23,13 @@ let
       url = "https://github.com/NixOS/nixpkgs/pull/77450.diff";
       sha256 = "13ikg7chpbf6rrg5sngbdb95q3awhdgl4g8vci42xmqyf208hzzd";
     }
+    { meta.description = "transmission: apply RFC0042 and harden the service";
+      url = "https://github.com/NixOS/nixpkgs/pull/92106.diff";
+      sha256 = "0h1105qy0wrirvi9fk5d00qsjvm745196vb7wgr648d56rm17vv1";
+    }
   ];
   localNixpkgsPatches = [
+    #nixpkgs/patches/apparmor.diff
   ];
   # Build nixpkgs with some patches.
   nixpkgs = originPkgs.applyPatches {