These were broken since 2016:f0367da7d1since StartLimitIntervalSec got moved into [Unit] from [Service]. StartLimitBurst has also been moved accordingly, so let's fix that one too. NixOS systems have been producing logs such as: /nix/store/wf98r55aszi1bkmln1lvdbp7znsfr70i-unit-caddy.service/caddy.service:31: Unknown key name 'StartLimitIntervalSec' in section 'Service', ignoring. I have also removed some unnecessary duplication in units disabling rate limiting since setting either interval or burst to zero disables it (ad16158c10/src/basic/ratelimit.c (L16))
		
			
				
	
	
		
			162 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
{ 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 = "/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 = 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 <varname>python3Packages</varname> as the sole argument.
 | 
						|
        '';
 | 
						|
        example = literalExample ''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}"
 | 
						|
    ));
 | 
						|
 | 
						|
  };
 | 
						|
}
 |