From b150e08169d95c5813ded16959002da38282d850 Mon Sep 17 00:00:00 2001 From: Martin Milata Date: Tue, 11 Feb 2020 17:52:48 +0100 Subject: [PATCH 1/3] nixos/supybot: stateDir in /var/lib, use tmpfiles Moving the stateDir is needed in order to use ProtectSystem=strict systemd option. --- nixos/modules/services/networking/supybot.nix | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/nixos/modules/services/networking/supybot.nix b/nixos/modules/services/networking/supybot.nix index d5b9a97a1c1..21e9fbd6e60 100644 --- a/nixos/modules/services/networking/supybot.nix +++ b/nixos/modules/services/networking/supybot.nix @@ -20,15 +20,18 @@ in }; stateDir = mkOption { - # Setting this to /var/lib/supybot caused useradd to fail - default = "/home/supybot"; + type = types.path; + default = if versionAtLeast config.system.stateVersion "20.09" + then "/var/lib/supybot" + else "/home/supybot"; + defaultText = "/var/lib/supybot"; description = "The root directory, logs and plugins are stored here"; }; configFile = mkOption { type = types.path; description = '' - Path to a supybot config file. This can be generated by + Path to initial supybot config file. This can be generated by running supybot-wizard. Note: all paths should include the full path to the stateDir @@ -50,7 +53,7 @@ in group = "supybot"; description = "Supybot IRC bot user"; home = cfg.stateDir; - createHome = true; + isSystemUser = true; }; users.groups.supybot = { @@ -63,11 +66,8 @@ in wantedBy = [ "multi-user.target" ]; path = [ pkgs.pythonPackages.limnoria ]; preStart = '' - cd ${cfg.stateDir} - mkdir -p backup conf data plugins logs/plugins tmp web - ln -sf ${cfg.configFile} supybot.cfg # This needs to be created afresh every time - rm -f supybot.cfg.bak + rm -f '${cfg.stateDir}/supybot.cfg.bak' ''; serviceConfig = { @@ -82,5 +82,18 @@ in }; }; + systemd.tmpfiles.rules = [ + "d '${cfg.stateDir}' 0700 supybot supybot - -" + "d '${cfg.stateDir}/backup' 0750 supybot supybot - -" + "d '${cfg.stateDir}/conf' 0750 supybot supybot - -" + "d '${cfg.stateDir}/data' 0750 supybot supybot - -" + "d '${cfg.stateDir}/plugins' 0750 supybot supybot - -" + "d '${cfg.stateDir}/logs' 0750 supybot supybot - -" + "d '${cfg.stateDir}/logs/plugins' 0750 supybot supybot - -" + "d '${cfg.stateDir}/tmp' 0750 supybot supybot - -" + "d '${cfg.stateDir}/web' 0750 supybot supybot - -" + "L '${cfg.stateDir}/supybot.cfg' - - - - ${cfg.configFile}" + ]; + }; } From 57f5fb62d4bccbf758a766fe11ce662cd07726ea Mon Sep 17 00:00:00 2001 From: Martin Milata Date: Tue, 11 Feb 2020 17:55:03 +0100 Subject: [PATCH 2/3] nixos/supybot: enable systemd sandboxing options --- nixos/doc/manual/release-notes/rl-2009.xml | 10 ++++++ nixos/modules/services/networking/supybot.nix | 33 ++++++++++++++++--- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/nixos/doc/manual/release-notes/rl-2009.xml b/nixos/doc/manual/release-notes/rl-2009.xml index 892208b01d7..c2149a68703 100644 --- a/nixos/doc/manual/release-notes/rl-2009.xml +++ b/nixos/doc/manual/release-notes/rl-2009.xml @@ -72,6 +72,16 @@ } + + + The supybot module now uses /var/lib/supybot + as its default stateDir path if stateVersion + is 20.09 or higher. It also enables number of + systemd sandboxing options + which may possibly interfere with some plugins. If this is the case you can disable the options through attributes in + . + + diff --git a/nixos/modules/services/networking/supybot.nix b/nixos/modules/services/networking/supybot.nix index 21e9fbd6e60..fabe4d0cb2b 100644 --- a/nixos/modules/services/networking/supybot.nix +++ b/nixos/modules/services/networking/supybot.nix @@ -3,13 +3,11 @@ with lib; let - cfg = config.services.supybot; - + isStateDirHome = hasPrefix "/home/" cfg.stateDir; + isStateDirVar = cfg.stateDir == "/var/lib/supybot"; in - { - options = { services.supybot = { @@ -43,7 +41,6 @@ in }; - config = mkIf cfg.enable { environment.systemPackages = [ pkgs.pythonPackages.limnoria ]; @@ -79,6 +76,32 @@ in Restart = "on-abort"; StartLimitInterval = "5m"; StartLimitBurst = "1"; + + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateTmp = true; + ProtectControlGroups = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + RestrictNamespaces = true; + RestrictRealtime = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + RemoveIPC = true; + ProtectHostname = true; + CapabilityBoundingSet = ""; + ProtectSystem = "full"; + } + // optionalAttrs isStateDirVar { + StateDirectory = "supybot"; + ProtectSystem = "strict"; + } + // optionalAttrs (!isStateDirHome) { + ProtectHome = true; }; }; From 1affd47cc10e62f5c58e106c9135e86c0c898a5f Mon Sep 17 00:00:00 2001 From: Martin Milata Date: Tue, 11 Feb 2020 17:56:03 +0100 Subject: [PATCH 3/3] nixos/supybot: python3 switch, add plugin options Python2 seems to be no longer supported by limnoria upstream. --- nixos/modules/services/networking/supybot.nix | 49 +++++++++++++++++-- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/nixos/modules/services/networking/supybot.nix b/nixos/modules/services/networking/supybot.nix index fabe4d0cb2b..dc9fb31ffd0 100644 --- a/nixos/modules/services/networking/supybot.nix +++ b/nixos/modules/services/networking/supybot.nix @@ -6,6 +6,7 @@ let cfg = config.services.supybot; isStateDirHome = hasPrefix "/home/" cfg.stateDir; isStateDirVar = cfg.stateDir == "/var/lib/supybot"; + pyEnv = pkgs.python3.withPackages (p: [ p.limnoria ] ++ (cfg.extraPackages p)); in { options = { @@ -13,8 +14,9 @@ in services.supybot = { enable = mkOption { + type = types.bool; default = false; - description = "Enable Supybot, an IRC bot"; + description = "Enable Supybot, an IRC bot (also known as Limnoria)."; }; stateDir = mkOption { @@ -37,13 +39,47 @@ in ''; }; + plugins = mkOption { + type = types.attrsOf types.path; + default = {}; + description = '' + Attribute set of additional plugins that will be symlinked to the + plugin subdirectory. + + Please note that you still need to add the plugins to the config + file (or with !load) using their attribute name. + ''; + example = literalExample '' + let + plugins = pkgs.fetchzip { + url = "https://github.com/ProgVal/Supybot-plugins/archive/57c2450c.zip"; + sha256 = "077snf84ibnva3sbpzdfpfma6hcdw7dflwnhg6pw7mgnf0nd84qd"; + }; + in + { + Wikipedia = "''${plugins}/Wikipedia"; + Decide = ./supy-decide; + } + ''; + }; + + extraPackages = mkOption { + default = p: []; + description = '' + Extra Python packages available to supybot plugins. The + value must be a function which receives the attrset defined + in python3Packages as the sole argument. + ''; + example = literalExample ''p: [ p.lxml p.requests ]''; + }; + }; }; config = mkIf cfg.enable { - environment.systemPackages = [ pkgs.pythonPackages.limnoria ]; + environment.systemPackages = [ pkgs.python3Packages.limnoria ]; users.users.supybot = { uid = config.ids.uids.supybot; @@ -59,16 +95,16 @@ in systemd.services.supybot = { description = "Supybot, an IRC bot"; + documentation = [ "https://limnoria.readthedocs.io/" ]; after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; - path = [ pkgs.pythonPackages.limnoria ]; preStart = '' # This needs to be created afresh every time rm -f '${cfg.stateDir}/supybot.cfg.bak' ''; serviceConfig = { - ExecStart = "${pkgs.pythonPackages.limnoria}/bin/supybot ${cfg.stateDir}/supybot.cfg"; + ExecStart = "${pyEnv}/bin/supybot ${cfg.stateDir}/supybot.cfg"; PIDFile = "/run/supybot.pid"; User = "supybot"; Group = "supybot"; @@ -116,7 +152,10 @@ in "d '${cfg.stateDir}/tmp' 0750 supybot supybot - -" "d '${cfg.stateDir}/web' 0750 supybot supybot - -" "L '${cfg.stateDir}/supybot.cfg' - - - - ${cfg.configFile}" - ]; + ] + ++ (flip mapAttrsToList cfg.plugins (name: dest: + "L+ '${cfg.stateDir}/plugins/${name}' - - - - ${dest}" + )); }; }