diff options
Diffstat (limited to 'nixos/modules/services/networking/supybot.nix')
-rw-r--r-- | nixos/modules/services/networking/supybot.nix | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/nixos/modules/services/networking/supybot.nix b/nixos/modules/services/networking/supybot.nix new file mode 100644 index 00000000000..94b79c7e247 --- /dev/null +++ b/nixos/modules/services/networking/supybot.nix @@ -0,0 +1,163 @@ +{ config, lib, pkgs, ... }: + +with lib; + +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 = { + + services.supybot = { + + enable = mkOption { + type = types.bool; + default = false; + description = "Enable Supybot, an IRC bot (also known as Limnoria)."; + }; + + stateDir = mkOption { + type = types.path; + default = if versionAtLeast config.system.stateVersion "20.09" + then "/var/lib/supybot" + else "/home/supybot"; + defaultText = literalExpression "/var/lib/supybot"; + description = "The root directory, logs and plugins are stored here"; + }; + + configFile = mkOption { + type = types.path; + description = '' + 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 + directory (backup conf data logs logs/plugins plugins tmp web). + ''; + }; + + plugins = mkOption { + type = types.attrsOf types.path; + default = {}; + description = '' + Attribute set of additional plugins that will be symlinked to the + <filename>plugin</filename> subdirectory. + + Please note that you still need to add the plugins to the config + file (or with <literal>!load</literal>) using their attribute name. + ''; + example = literalExpression '' + 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 { + type = types.functionTo (types.listOf types.package); + default = p: []; + defaultText = literalExpression "p: []"; + description = '' + Extra Python packages available to supybot plugins. The + value must be a function which receives the attrset defined + in <varname>python3Packages</varname> as the sole argument. + ''; + example = literalExpression "p: [ p.lxml p.requests ]"; + }; + + }; + + }; + + config = mkIf cfg.enable { + + environment.systemPackages = [ pkgs.python3Packages.limnoria ]; + + users.users.supybot = { + uid = config.ids.uids.supybot; + group = "supybot"; + description = "Supybot IRC bot user"; + home = cfg.stateDir; + isSystemUser = true; + }; + + users.groups.supybot = { + gid = config.ids.gids.supybot; + }; + + systemd.services.supybot = { + description = "Supybot, an IRC bot"; + documentation = [ "https://limnoria.readthedocs.io/" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + preStart = '' + # This needs to be created afresh every time + rm -f '${cfg.stateDir}/supybot.cfg.bak' + ''; + + startLimitIntervalSec = 5 * 60; # 5 min + startLimitBurst = 1; + serviceConfig = { + ExecStart = "${pyEnv}/bin/supybot ${cfg.stateDir}/supybot.cfg"; + PIDFile = "/run/supybot.pid"; + User = "supybot"; + Group = "supybot"; + UMask = "0007"; + Restart = "on-abort"; + + 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; + }; + }; + + 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}" + ] + ++ (flip mapAttrsToList cfg.plugins (name: dest: + "L+ '${cfg.stateDir}/plugins/${name}' - - - - ${dest}" + )); + + }; +} |