3
0
Fork 0
forked from mirrors/nixpkgs

nixos: transmission improvements

This mostly upgrades transmission, and does some very minor touchups on
AppArmor support.

In particular, there is now no need to ever specify the umask as part of
the settings, as it will be mixed in by default (which is essentially
always what you want). Also, the default configuration is now more
sensible: Downloads are put in /var/lib/transmission/Downloads, and
incomplete files are put in /var/lib/transmission/.incomplete - this
also allows easy use of file syncing probrams, like BitTorrent Sync.

Finally, this unconditionally enables the AppArmor profiles for the
daemon, if AppArmor is enabled - rather than letting the user specify
profile support, it's best to default to supporting profiles for daemons
transparently in all places.

Signed-off-by: Austin Seipp <aseipp@pobox.com>
This commit is contained in:
Austin Seipp 2014-04-15 06:50:39 -05:00
parent 253f83ea2d
commit da6bc44dd7
2 changed files with 38 additions and 78 deletions
nixos/modules
security
services/torrent

View file

@ -1,55 +1,39 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
with lib;
let let
cfg = config.security.apparmor; cfg = config.security.apparmor;
in in
with lib;
{ {
###### interface
options = { options = {
security.apparmor = { security.apparmor = {
enable = mkOption { enable = mkOption {
type = types.bool; type = types.bool;
default = false; default = false;
description = '' description = "Enable the AppArmor Mandatory Access Control system.";
Enable AppArmor application security system. Enable only if
you want to further improve AppArmor.
'';
}; };
profiles = mkOption { profiles = mkOption {
type = types.listOf types.path; type = types.listOf types.path;
default = []; default = [];
description = '' description = "List of files containing AppArmor profiles.";
List of file names of AppArmor profiles.
'';
}; };
}; };
}; };
config = mkIf cfg.enable {
###### implementation assertions =
[ { assertion = config.boot.kernelPackages.kernel.features ? apparmor
config = mkIf (cfg.enable) { && config.boot.kernelPackages.kernel.features.apparmor;
message = "Your selected kernel does not have AppArmor support";
assertions = [ { assertion = config.boot.kernelPackages.kernel.features ? apparmor }
&& config.boot.kernelPackages.kernel.features.apparmor; ];
message = "AppArmor is enabled, but the kernel doesn't have AppArmor support"; }
];
environment.systemPackages = [ pkgs.apparmor ]; environment.systemPackages = [ pkgs.apparmor ];
systemd.services.apparmor = { systemd.services.apparmor = {
#wantedBy = [ "basic.target" ];
wantedBy = [ "local-fs.target" ]; wantedBy = [ "local-fs.target" ];
path = [ pkgs.apparmor ]; path = [ pkgs.apparmor ];
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
@ -61,9 +45,6 @@ with lib;
''${pkgs.apparmor}/sbin/apparmor_parser -Rv -I ${pkgs.apparmor}/etc/apparmor.d/ "${profile}" ; '' ''${pkgs.apparmor}/sbin/apparmor_parser -Rv -I ${pkgs.apparmor}/etc/apparmor.d/ "${profile}" ; ''
) cfg.profiles; ) cfg.profiles;
}; };
}; };
}; };
} }

View file

@ -1,13 +1,14 @@
# NixOS module for Transmission BitTorrent daemon
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
with lib; with lib;
let let
cfg = config.services.transmission; cfg = config.services.transmission;
apparmor = config.security.apparmor.enable;
homeDir = "/var/lib/transmission"; homeDir = "/var/lib/transmission";
downloadDir = "${homeDir}/Downloads";
incompleteDir = "${homeDir}/.incomplete";
settingsDir = "${homeDir}/.config/transmission-daemon"; settingsDir = "${homeDir}/.config/transmission-daemon";
settingsFile = "${settingsDir}/settings.json"; settingsFile = "${settingsDir}/settings.json";
@ -31,16 +32,12 @@ let
(if isList value then value else [value])) (if isList value then value else [value]))
as)); as));
# for users in group "transmission" to have access to torrents
fullSettings = cfg.settings // { umask = 2; };
in in
{ {
### configuration
options = { options = {
services.transmission = { services.transmission = {
enable = mkOption { enable = mkOption {
type = types.uniq types.bool; type = types.uniq types.bool;
default = false; default = false;
@ -59,65 +56,48 @@ in
type = types.attrs; type = types.attrs;
default = default =
{ {
# for users in group "transmission" to have access to torrents download-dir = downloadDir;
umask = 2; incomplete-dir = incompleteDir;
} incomplete-dir-enabled = true;
; };
example = example =
{ {
download-dir = "/srv/torrents/"; download-dir = "/srv/torrents/";
incomplete-dir = "/srv/torrents/.incomplete/"; incomplete-dir = "/srv/torrents/.incomplete/";
incomplete-dir-enabled = true; incomplete-dir-enabled = true;
rpc-whitelist = "127.0.0.1,192.168.*.*"; rpc-whitelist = "127.0.0.1,192.168.*.*";
# for users in group "transmission" to have access to torrents };
umask = 2;
}
;
description = '' description = ''
Attribute set whos fields overwrites fields in settings.json (each Attribute set whos fields overwrites fields in settings.json (each
time the service starts). String values must be quoted, integer and time the service starts). String values must be quoted, integer and
boolean values must not. boolean values must not.
See https://trac.transmissionbt.com/wiki/EditConfigFiles for documentation See https://trac.transmissionbt.com/wiki/EditConfigFiles for
and/or look at ${settingsFile}." documentation and/or look at ${settingsFile}.
''; '';
}; };
rpc_port = mkOption { port = mkOption {
type = types.uniq types.int; type = types.uniq types.int;
default = 9091; default = 9091;
description = "TCP port number to run the RPC/web interface."; description = "TCP port number to run the RPC/web interface.";
}; };
apparmor = mkOption {
type = types.uniq types.bool;
default = true;
description = "Generate apparmor profile for transmission-daemon.";
};
}; };
}; };
### implementation
config = mkIf cfg.enable { config = mkIf cfg.enable {
systemd.services.transmission = { systemd.services.transmission = {
description = "Transmission BitTorrent Daemon"; description = "Transmission BitTorrent Service";
after = [ "network.target" ] ++ optional (config.security.apparmor.enable && cfg.apparmor) "apparmor.service"; after = [ "network.target" ] ++ optional apparmor "apparmor.service";
requires = mkIf (config.security.apparmor.enable && cfg.apparmor) [ "apparmor.service" ]; requires = mkIf apparmor [ "apparmor.service" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
# 1) Only the "transmission" user and group have access to torrents. # 1) Only the "transmission" user and group have access to torrents.
# 2) Optionally update/force specific fields into the configuration file. # 2) Optionally update/force specific fields into the configuration file.
serviceConfig.ExecStartPre = serviceConfig.ExecStartPre = ''
if cfg.settings != {} then '' ${pkgs.stdenv.shell} -c "chmod 770 ${homeDir} && mkdir -p ${settingsDir} ${downloadDir} ${incompleteDir} && ${pkgs.transmission}/bin/transmission-daemon -d |& sed ${attrsToSedArgs fullSettings} > ${settingsFile}.tmp && mv ${settingsFile}.tmp ${settingsFile}"
${pkgs.stdenv.shell} -c "chmod 770 ${homeDir} && mkdir -p ${settingsDir} && ${pkgs.transmission}/bin/transmission-daemon -d |& sed ${attrsToSedArgs cfg.settings} > ${settingsFile}.tmp && mv ${settingsFile}.tmp ${settingsFile}" '';
'' serviceConfig.ExecStart = "${pkgs.transmission}/bin/transmission-daemon -f --port ${toString config.services.transmission.port}";
else ''
${pkgs.stdenv.shell} -c "chmod 770 ${homeDir}"
'';
serviceConfig.ExecStart = "${pkgs.transmission}/bin/transmission-daemon -f --port ${toString config.services.transmission.rpc_port}";
serviceConfig.ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; serviceConfig.ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
serviceConfig.User = "transmission"; serviceConfig.User = "transmission";
# NOTE: transmission has an internal umask that also must be set (in settings.json) # NOTE: transmission has an internal umask that also must be set (in settings.json)
@ -127,6 +107,7 @@ in
# It's useful to have transmission in path, e.g. for remote control # It's useful to have transmission in path, e.g. for remote control
environment.systemPackages = [ pkgs.transmission ]; environment.systemPackages = [ pkgs.transmission ];
users.extraGroups.transmission.gid = config.ids.gids.transmission;
users.extraUsers.transmission = { users.extraUsers.transmission = {
group = "transmission"; group = "transmission";
uid = config.ids.uids.transmission; uid = config.ids.uids.transmission;
@ -135,10 +116,8 @@ in
createHome = true; createHome = true;
}; };
users.extraGroups.transmission.gid = config.ids.gids.transmission;
# AppArmor profile # AppArmor profile
security.apparmor.profiles = mkIf (config.security.apparmor.enable && cfg.apparmor) [ security.apparmor.profiles = mkIf apparmor [
(pkgs.writeText "apparmor-transmission-daemon" '' (pkgs.writeText "apparmor-transmission-daemon" ''
#include <tunables/global> #include <tunables/global>
@ -161,9 +140,9 @@ in
owner ${settingsDir}/** rw, owner ${settingsDir}/** rw,
${cfg.settings.download-dir}/** rw, ${fullSettings.download-dir}/** rw,
${optionalString cfg.settings.incomplete-dir-enabled '' ${optionalString fullSettings.incomplete-dir-enabled ''
${cfg.settings.incomplete-dir}/** rw, ${fullSettings.incomplete-dir}/** rw,
''} ''}
} }
'') '')