270 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
		
		
			
		
	
	
			270 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
|   | { config, lib, pkgs, options, ... }: | |||
|  | with lib; | |||
|  | let | |||
|  |   cfg = config.services.biboumi; | |||
|  |   inherit (config.environment) etc; | |||
|  |   rootDir = "/run/biboumi/mnt-root"; | |||
|  |   stateDir = "/var/lib/biboumi"; | |||
|  |   settingsFile = pkgs.writeText "biboumi.cfg" ( | |||
|  |     generators.toKeyValue { | |||
|  |       mkKeyValue = k: v: | |||
|  |         if v == null then "" | |||
|  |         else generators.mkKeyValueDefault {} "=" k v; | |||
|  |     } cfg.settings); | |||
|  |   need_CAP_NET_BIND_SERVICE = cfg.settings.identd_port != 0 && cfg.settings.identd_port < 1024; | |||
|  | in | |||
|  | { | |||
|  |   options = { | |||
|  |     services.biboumi = { | |||
|  |       enable = mkEnableOption "the Biboumi XMPP gateway to IRC"; | |||
|  | 
 | |||
|  |       settings = mkOption { | |||
|  |         description = ''
 | |||
|  |           See <link xlink:href="https://lab.louiz.org/louiz/biboumi/blob/8.5/doc/biboumi.1.rst">biboumi 8.5</link> | |||
|  |           for documentation. | |||
|  |         '';
 | |||
|  |         default = {}; | |||
|  |         type = types.submodule { | |||
|  |           freeformType = with types; | |||
|  |             (attrsOf (nullOr (oneOf [str int bool]))) // { | |||
|  |               description = "settings option"; | |||
|  |             }; | |||
|  |           options.admin = mkOption { | |||
|  |             type = with types; listOf str; | |||
|  |             default = []; | |||
|  |             example = ["admin@example.org"]; | |||
|  |             apply = concatStringsSep ":"; | |||
|  |             description = ''
 | |||
|  |               The bare JID of the gateway administrator. This JID will have more | |||
|  |               privileges than other standard users, for example some administration | |||
|  |               ad-hoc commands will only be available to that JID. | |||
|  |             '';
 | |||
|  |           }; | |||
|  |           options.ca_file = mkOption { | |||
|  |             type = types.path; | |||
|  |             default = "/etc/ssl/certs/ca-certificates.crt"; | |||
|  |             description = ''
 | |||
|  |               Specifies which file should be used as the list of trusted CA | |||
|  |               when negociating a TLS session. | |||
|  |             '';
 | |||
|  |           }; | |||
|  |           options.db_name = mkOption { | |||
|  |             type = with types; either path str; | |||
|  |             default = "${stateDir}/biboumi.sqlite"; | |||
|  |             description = ''
 | |||
|  |               The name of the database to use. | |||
|  |             '';
 | |||
|  |             example = "postgresql://user:secret@localhost"; | |||
|  |           }; | |||
|  |           options.hostname = mkOption { | |||
|  |             type = types.str; | |||
|  |             example = "biboumi.example.org"; | |||
|  |             description = ''
 | |||
|  |               The hostname served by the XMPP gateway. | |||
|  |               This domain must be configured in the XMPP server | |||
|  |               as an external component. | |||
|  |             '';
 | |||
|  |           }; | |||
|  |           options.identd_port = mkOption { | |||
|  |             type = types.port; | |||
|  |             default = 113; | |||
|  |             example = 0; | |||
|  |             description = ''
 | |||
|  |               The TCP port on which to listen for identd queries. | |||
|  |             '';
 | |||
|  |           }; | |||
|  |           options.log_level = mkOption { | |||
|  |             type = types.ints.between 0 3; | |||
|  |             default = 1; | |||
|  |             description = ''
 | |||
|  |               Indicate what type of log messages to write in the logs. | |||
|  |               0 is debug, 1 is info, 2 is warning, 3 is error. | |||
|  |             '';
 | |||
|  |           }; | |||
|  |           options.password = mkOption { | |||
|  |             type = with types; nullOr str; | |||
|  |             description = ''
 | |||
|  |               The password used to authenticate the XMPP component to your XMPP server. | |||
|  |               This password must be configured in the XMPP server, | |||
|  |               associated with the external component on | |||
|  |               <link linkend="opt-services.biboumi.settings.hostname">hostname</link>. | |||
|  | 
 | |||
|  |               Set it to null and use <link linkend="opt-services.biboumi.credentialsFile">credentialsFile</link> | |||
|  |               if you do not want this password to go into the Nix store. | |||
|  |             '';
 | |||
|  |           }; | |||
|  |           options.persistent_by_default = mkOption { | |||
|  |             type = types.bool; | |||
|  |             default = false; | |||
|  |             description = ''
 | |||
|  |               Whether all rooms will be persistent by default: | |||
|  |               the value of the “persistent” option in the global configuration of each | |||
|  |               user will be “true”, but the value of each individual room will still | |||
|  |               default to false. This means that a user just needs to change the global | |||
|  |               “persistent” configuration option to false in order to override this. | |||
|  |             '';
 | |||
|  |           }; | |||
|  |           options.policy_directory = mkOption { | |||
|  |             type = types.path; | |||
|  |             default = "${pkgs.biboumi}/etc/biboumi"; | |||
|  |             description = ''
 | |||
|  |               A directory that should contain the policy files, | |||
|  |               used to customize Botan’s behaviour | |||
|  |               when negociating the TLS connections with the IRC servers. | |||
|  |             '';
 | |||
|  |           }; | |||
|  |           options.port = mkOption { | |||
|  |             type = types.port; | |||
|  |             default = 5347; | |||
|  |             description = ''
 | |||
|  |               The TCP port to use to connect to the local XMPP component. | |||
|  |             '';
 | |||
|  |           }; | |||
|  |           options.realname_customization = mkOption { | |||
|  |             type = types.bool; | |||
|  |             default = true; | |||
|  |             description = ''
 | |||
|  |               Whether the users will be able to use | |||
|  |               the ad-hoc commands that lets them configure | |||
|  |               their realname and username. | |||
|  |             '';
 | |||
|  |           }; | |||
|  |           options.realname_from_jid = mkOption { | |||
|  |             type = types.bool; | |||
|  |             default = false; | |||
|  |             description = ''
 | |||
|  |               Whether the realname and username of each biboumi | |||
|  |               user will be extracted from their JID. | |||
|  |               Otherwise they will be set to the nick | |||
|  |               they used to connect to the IRC server. | |||
|  |             '';
 | |||
|  |           }; | |||
|  |           options.xmpp_server_ip = mkOption { | |||
|  |             type = types.str; | |||
|  |             default = "127.0.0.1"; | |||
|  |             description = ''
 | |||
|  |               The IP address to connect to the XMPP server on. | |||
|  |               The connection to the XMPP server is unencrypted, | |||
|  |               so the biboumi instance and the server should | |||
|  |               normally be on the same host. | |||
|  |             '';
 | |||
|  |           }; | |||
|  |         }; | |||
|  |       }; | |||
|  | 
 | |||
|  |       credentialsFile = mkOption { | |||
|  |         type = types.path; | |||
|  |         description = ''
 | |||
|  |           Path to a configuration file to be merged with the settings. | |||
|  |           Beware not to surround "=" with spaces when setting biboumi's options in this file. | |||
|  |           Useful to merge a file which is better kept out of the Nix store | |||
|  |           because it contains sensible data like | |||
|  |           <link linkend="opt-services.biboumi.settings.password">password</link>. | |||
|  |         '';
 | |||
|  |         default = "/dev/null"; | |||
|  |         example = "/run/keys/biboumi.cfg"; | |||
|  |       }; | |||
|  | 
 | |||
|  |       openFirewall = mkEnableOption "opening of the identd port in the firewall"; | |||
|  |     }; | |||
|  |   }; | |||
|  | 
 | |||
|  |   config = mkIf cfg.enable { | |||
|  |     networking.firewall = mkIf (cfg.openFirewall && cfg.settings.identd_port != 0) | |||
|  |       { allowedTCPPorts = [ cfg.settings.identd_port ]; }; | |||
|  | 
 | |||
|  |     systemd.services.biboumi = { | |||
|  |       description = "Biboumi, XMPP to IRC gateway"; | |||
|  |       after = [ "network.target" ]; | |||
|  |       wantedBy = [ "multi-user.target" ]; | |||
|  | 
 | |||
|  |       serviceConfig = { | |||
|  |         Type = "notify"; | |||
|  |         # Biboumi supports systemd's watchdog. | |||
|  |         WatchdogSec = 20; | |||
|  |         Restart = "always"; | |||
|  |         # Use "+" because credentialsFile may not be accessible to User= or Group=. | |||
|  |         ExecStartPre = [("+" + pkgs.writeShellScript "biboumi-prestart" ''
 | |||
|  |           set -eux | |||
|  |           cat ${settingsFile} '${cfg.credentialsFile}' | | |||
|  |           install -m 644 /dev/stdin /run/biboumi/biboumi.cfg | |||
|  |         '')];
 | |||
|  |         ExecStart = "${pkgs.biboumi}/bin/biboumi /run/biboumi/biboumi.cfg"; | |||
|  |         ExecReload = "${pkgs.coreutils}/bin/kill -USR1 $MAINPID"; | |||
|  |         # Firewalls needing opening for output connections can still do that | |||
|  |         # selectively for biboumi with: | |||
|  |         # users.users.biboumi.isSystemUser = true; | |||
|  |         # and, for example: | |||
|  |         # networking.nftables.ruleset = '' | |||
|  |         #   add rule inet filter output meta skuid biboumi tcp accept | |||
|  |         # ''; | |||
|  |         DynamicUser = true; | |||
|  |         RootDirectory = rootDir; | |||
|  |         RootDirectoryStartOnly = true; | |||
|  |         InaccessiblePaths = [ "-+${rootDir}" ]; | |||
|  |         RuntimeDirectory = [ "biboumi" (removePrefix "/run/" rootDir) ]; | |||
|  |         RuntimeDirectoryMode = "700"; | |||
|  |         StateDirectory = "biboumi"; | |||
|  |         StateDirectoryMode = "700"; | |||
|  |         MountAPIVFS = true; | |||
|  |         UMask = "0066"; | |||
|  |         BindPaths = [ | |||
|  |           stateDir | |||
|  |           # This is for Type="notify" | |||
|  |           # See https://github.com/systemd/systemd/issues/3544 | |||
|  |           "/run/systemd/notify" | |||
|  |           "/run/systemd/journal/socket" | |||
|  |         ]; | |||
|  |         BindReadOnlyPaths = [ | |||
|  |           builtins.storeDir | |||
|  |           "/etc" | |||
|  |         ]; | |||
|  |         # The following options are only for optimizing: | |||
|  |         # systemd-analyze security biboumi | |||
|  |         AmbientCapabilities = [ (optionalString need_CAP_NET_BIND_SERVICE "CAP_NET_BIND_SERVICE") ]; | |||
|  |         CapabilityBoundingSet = [ (optionalString need_CAP_NET_BIND_SERVICE "CAP_NET_BIND_SERVICE") ]; | |||
|  |         # ProtectClock= adds DeviceAllow=char-rtc r | |||
|  |         DeviceAllow = ""; | |||
|  |         LockPersonality = true; | |||
|  |         MemoryDenyWriteExecute = true; | |||
|  |         NoNewPrivileges = true; | |||
|  |         PrivateDevices = true; | |||
|  |         PrivateMounts = true; | |||
|  |         PrivateNetwork = mkDefault false; | |||
|  |         PrivateTmp = true; | |||
|  |         # PrivateUsers=true breaks AmbientCapabilities=CAP_NET_BIND_SERVICE | |||
|  |         # See https://bugs.archlinux.org/task/65921 | |||
|  |         PrivateUsers = !need_CAP_NET_BIND_SERVICE; | |||
|  |         ProtectClock = true; | |||
|  |         ProtectControlGroups = true; | |||
|  |         ProtectHome = true; | |||
|  |         ProtectHostname = true; | |||
|  |         ProtectKernelLogs = true; | |||
|  |         ProtectKernelModules = true; | |||
|  |         ProtectKernelTunables = true; | |||
|  |         ProtectSystem = "strict"; | |||
|  |         RemoveIPC = true; | |||
|  |         # AF_UNIX is for /run/systemd/notify | |||
|  |         RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; | |||
|  |         RestrictNamespaces = true; | |||
|  |         RestrictRealtime = true; | |||
|  |         RestrictSUIDSGID = true; | |||
|  |         SystemCallFilter = [ | |||
|  |           "@system-service" | |||
|  |           # Groups in @system-service which do not contain a syscall | |||
|  |           # listed by perf stat -e 'syscalls:sys_enter_*' biboumi biboumi.cfg | |||
|  |           # in tests, and seem likely not necessary for biboumi. | |||
|  |           # To run such a perf in ExecStart=, you have to: | |||
|  |           # - AmbientCapabilities="CAP_SYS_ADMIN" | |||
|  |           # - mount -o remount,mode=755 /sys/kernel/debug/{,tracing} | |||
|  |           "~@aio" "~@chown" "~@ipc" "~@keyring" "~@resources" "~@setuid" "~@timer" | |||
|  |         ]; | |||
|  |         SystemCallArchitectures = "native"; | |||
|  |         SystemCallErrorNumber = "EPERM"; | |||
|  |       }; | |||
|  |     }; | |||
|  |   }; | |||
|  | 
 | |||
|  |   meta.maintainers = with maintainers; [ julm ]; | |||
|  | } |