{ config, lib, pkgs, ... }: let cfg = config.services.wastebin; inherit (lib) mkEnableOption mkPackageOption mkIf mkOption types mapAttrs isBool getExe boolToString optionalAttrs; in { options.services.wastebin = { enable = mkEnableOption "Wastenbin pastebin service"; package = mkPackageOption pkgs "wastebin" { }; stateDir = mkOption { type = types.path; default = "/var/lib/wastebin"; description = "State directory of the daemon."; }; secretFile = mkOption { type = types.nullOr types.path; default = null; example = "/run/secrets/wastebin.env"; description = '' Path to file containing sensitive environment variables. Some variables that can be considered secrets are: - WASTEBIN_PASSWORD_SALT: salt used to hash user passwords used for encrypting pastes. - WASTEBIN_SIGNING_KEY: sets the key to sign cookies. If not set, a random key will be generated which means cookies will become invalid after restarts and paste creators will not be able to delete their pastes anymore. ''; }; settings = mkOption { description = '' Additional configuration for wastebin, see for supported values. For secrets use secretFile option instead. ''; type = types.submodule { freeformType = with types; attrsOf (oneOf [ bool int str ]); options = { WASTEBIN_ADDRESS_PORT = mkOption { type = types.str; default = "0.0.0.0:8088"; description = "Address and port to bind to"; }; WASTEBIN_BASE_URL = mkOption { default = "http://localhost"; example = "https://myhost.tld"; type = types.str; description = '' Base URL for the QR code display. If not set, the user agent's Host header field is used as an approximation. ''; }; WASTEBIN_CACHE_SIZE = mkOption { default = 128; type = types.int; description = "Number of rendered syntax highlight items to cache. Can be disabled by setting to 0."; }; WASTEBIN_DATABASE_PATH = mkOption { default = "/var/lib/wastebin/sqlite3.db"; # TODO make this default to stateDir/sqlite3.db type = types.str; description = "Path to the sqlite3 database file. If not set, an in-memory database is used."; }; WASTEBIN_HTTP_TIMEOUT = mkOption { default = 5; type = types.int; description = "Maximum number of seconds a request can be processed until wastebin responds with 408"; }; WASTEBIN_MAX_BODY_SIZE = mkOption { default = 1024; type = types.int; description = "Number of bytes to accept for POST requests"; }; WASTEBIN_TITLE = mkOption { default = "wastebin"; type = types.str; description = "Overrides the HTML page title"; }; RUST_LOG = mkOption { default = "info"; type = types.str; description = '' Influences logging. Besides the typical trace, debug, info etc. keys, you can also set the tower_http key to some log level to get additional information request and response logs. ''; }; }; }; default = { }; example = { WASTEBIN_TITLE = "My awesome pastebin"; }; }; }; config = mkIf cfg.enable { systemd.services.wastebin = { after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; environment = mapAttrs (_: v: if isBool v then boolToString v else toString v) cfg.settings; serviceConfig = { CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; DevicePolicy = "closed"; DynamicUser = true; ExecStart = "${getExe cfg.package}"; LockPersonality = true; MemoryDenyWriteExecute = true; PrivateDevices = true; PrivateUsers = true; ProtectClock = true; ProtectControlGroups = true; ProtectHostname = true; ProtectKernelLogs = true; ProtectKernelModules = true; ProtectKernelTunables = true; ProtectProc = "invisible"; RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; RestrictNamespaces = true; RestrictRealtime = true; SystemCallArchitectures = [ "native" ]; SystemCallFilter = [ "@system-service" ]; StateDirectory = baseNameOf cfg.stateDir; ReadWritePaths = cfg.stateDir; } // optionalAttrs (cfg.secretFile != null) { EnvironmentFile = cfg.secretFile; }; }; }; meta.maintainers = with lib.maintainers; [ pinpox ]; }