From 2de3c4bd7840af8bf6a37e06a1124e10db40ac20 Mon Sep 17 00:00:00 2001 From: SLNOS Date: Tue, 1 May 2018 00:00:00 +0000 Subject: [PATCH 1/2] nixos/tor: add tor-init service to fix directory ownerships, fix hardenings This reverts a part of 5bd12c694bfebaef1d03eb7f74a6eca01b86f546. Apparently there's no way to specify user for RuntimeDirectory in systemd service file (it's always root) but tor won't create control socket if the dir is owned by anybody except the tor user. These hardenings were adopted from the upstream service file, checked against systemd.service(5) and systemd.exec(5) manuals, and tested to actually work with all the options enabled. `PrivateDevices` implies `DevicePolicy=closed` according to systemd.exec(5), removed. `--RunAsDaemon 0` is the default value according to tor(5), removed. --- nixos/modules/services/security/tor.nix | 47 +++++++++++++++++-------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix index 806252f49b8..4f4f11907a7 100644 --- a/nixos/modules/services/security/tor.nix +++ b/nixos/modules/services/security/tor.nix @@ -695,19 +695,38 @@ in uid = config.ids.uids.tor; }; + # We have to do this instead of using RuntimeDirectory option in + # the service below because systemd has no way to set owners of + # RuntimeDirectory and putting this into the service below + # requires that service to relax it's sandbox since this needs + # writable /run + systemd.services.tor-init = + { description = "Tor Daemon Init"; + wantedBy = [ "tor.service" ]; + after = [ "local-fs.target" ]; + script = '' + install -m 0700 -o tor -g tor -d ${torDirectory} ${torDirectory}/onion + install -m 0750 -o tor -g tor -d ${torRunDirectory} + ''; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + }; + systemd.services.tor = { description = "Tor Daemon"; path = [ pkgs.tor ]; wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; + after = [ "tor-init.service" "network.target" ]; restartTriggers = [ torRcFile ]; serviceConfig = { Type = "simple"; # Translated from the upstream contrib/dist/tor.service.in ExecStartPre = "${pkgs.tor}/bin/tor -f ${torRcFile} --verify-config"; - ExecStart = "${pkgs.tor}/bin/tor -f ${torRcFile} --RunAsDaemon 0"; + ExecStart = "${pkgs.tor}/bin/tor -f ${torRcFile}"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; KillSignal = "SIGINT"; TimeoutSec = 30; @@ -715,20 +734,18 @@ in LimitNOFILE = 32768; # Hardening - # Note: DevicePolicy is set to 'closed', although the - # minimal permissions are really: - # DeviceAllow /dev/null rw - # DeviceAllow /dev/urandom r - # .. but we can't specify DeviceAllow multiple times. 'closed' - # is close enough. - RuntimeDirectory = "tor"; - StateDirectory = [ "tor" "tor/onion" ]; - PrivateTmp = "yes"; - DevicePolicy = "closed"; - InaccessibleDirectories = "/home"; - ReadOnlyDirectories = "/"; - ReadWriteDirectories = [torDirectory torRunDirectory]; + # this seems to unshare /run despite what systemd.exec(5) says + PrivateTmp = mkIf (!cfg.controlSocket.enable) "yes"; + PrivateDevices = "yes"; + ProtectHome = "yes"; + ProtectSystem = "strict"; + InaccessiblePaths = "/home"; + ReadOnlyPaths = "/"; + ReadWritePaths = [ torDirectory torRunDirectory ]; NoNewPrivileges = "yes"; + + # tor.service.in has this in, but this line it fails to spawn a namespace when using hidden services + #CapabilityBoundingSet = "CAP_SETUID CAP_SETGID CAP_NET_BIND_SERVICE"; }; }; From adab27a352fcd6956dc910fc09a5829c2000bc17 Mon Sep 17 00:00:00 2001 From: SLNOS Date: Tue, 1 May 2018 00:00:00 +0000 Subject: [PATCH 2/2] nixos/tor: use ControlPort for controlSocket for simplicity --- nixos/modules/services/security/tor.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nixos/modules/services/security/tor.nix b/nixos/modules/services/security/tor.nix index 4f4f11907a7..c2de1c45a39 100644 --- a/nixos/modules/services/security/tor.nix +++ b/nixos/modules/services/security/tor.nix @@ -39,7 +39,7 @@ let ''} ${optint "ControlPort" cfg.controlPort} - ${optionalString cfg.controlSocket.enable "ControlSocket ${torRunDirectory}/control GroupWritable RelaxDirModeCheck"} + ${optionalString cfg.controlSocket.enable "ControlPort unix:${torRunDirectory}/control GroupWritable RelaxDirModeCheck"} '' # Client connection config + optionalString cfg.client.enable ''