diff --git a/README.md b/README.md index 7537010..e3ddab7 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,11 @@ motd = "my image host, currently hosting %uplcount% files" # cached anyways. max_temp_lifetime = 43200 +# OPTIONAL - the maximum length (in bytes) a file being uploaded may be. +# A word of warning about this: the error shown to ShareX users who +# hit the limit is *not* very clear. ("connection closed" or similar) +max_upload_len = 2_147_483_648 + # The maximum length (in bytes) an image file may be before the server # will skip removing its EXIF data. # The performance impact of breeze's EXIF data removal is not @@ -108,7 +113,7 @@ upload_lifetime = 1800 scan_freq = 60 # How much memory (in bytes) the cache is allowed to consume. -mem_capacity = 4_294_967_295 +mem_capacity = 4_294_967_296 [http] # The address that the HTTP server will listen on. (ip:port) diff --git a/flake.nix b/flake.nix index 510ba3f..d92f2cd 100644 --- a/flake.nix +++ b/flake.nix @@ -7,14 +7,15 @@ flake-utils.url = "github:numtide/flake-utils"; }; - outputs = { - self, - nixpkgs, - crane, - flake-utils, - ... - }: - flake-utils.lib.eachDefaultSystem (system: let + outputs = + { self + , nixpkgs + , crane + , flake-utils + , ... + }: + flake-utils.lib.eachDefaultSystem (system: + let pkgs = nixpkgs.legacyPackages.${system}; craneLib = crane.mkLib pkgs; @@ -37,13 +38,14 @@ breeze = craneLib.buildPackage (commonArgs // { - cargoArtifacts = craneLib.buildDepsOnly commonArgs; + cargoArtifacts = craneLib.buildDepsOnly commonArgs; - # Additional environment variables or build phases/hooks can be set - # here *without* rebuilding all dependency crates - # MY_CUSTOM_VAR = "some value"; - }); - in { + # Additional environment variables or build phases/hooks can be set + # here *without* rebuilding all dependency crates + # MY_CUSTOM_VAR = "some value"; + }); + in + { checks = { inherit breeze; }; @@ -62,9 +64,129 @@ # MY_CUSTOM_DEVELOPMENT_VAR = "something else"; # Extra inputs can be added here; cargo and rustc are provided by default. - packages = [ - pkgs.rewrk + packages = with pkgs; [ + alejandra + rewrk ]; }; + + nixosModules.breeze = + { config + , pkgs + , lib + , ... + }: + with lib; let + cfg = config.services.breeze; + settingsFormat = pkgs.formats.toml { }; + in + { + options = { + services.breeze = { + enable = mkEnableOption "breeze file server"; + + package = mkOption { + type = types.package; + default = breeze; + description = "Package for `breeze` to use"; + }; + + user = mkOption { + type = types.str; + default = "breeze"; + description = "User that `breeze` will run under"; + }; + + group = mkOption { + type = types.str; + default = "breeze"; + description = "Group that `breeze` will run under"; + }; + + extraGroups = mkOption { + type = types.listOf types.str; + default = [ ]; + description = "Supplementary groups"; + }; + + settings = mkOption { + type = settingsFormat.type; + default = { }; + description = '' + The *.toml configuration to run `breeze` with. + There is no formal documentation, but there is an example in the [readme](https://git.min.rip/min/breeze/src/branch/main/README.md). + ''; + }; + }; + }; + + config = mkIf cfg.enable { + users.users.${cfg.user} = { + isSystemUser = true; + inherit (cfg) group; + }; + + users.groups.${cfg.group} = { }; + + systemd.services.breeze = { + description = "breeze file server"; + after = [ "local-fs.target" "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = rec { + User = cfg.user; + Group = cfg.group; + DynamicUser = false; # we write files, so don't do that + SupplementaryGroups = cfg.extraGroups; + StateDirectory = "breeze"; + CacheDirectory = "breeze"; + ExecStart = escapeShellArgs [ + "${cfg.package}/bin/breeze" + "--config" + (settingsFormat.generate "breeze.toml" cfg.settings) + ]; + Restart = "on-failure"; + + # Security Options # + + NoNewPrivileges = true; # implied by DynamicUser + RemoveIPC = true; # implied by DynamicUser + + AmbientCapabilities = ""; + CapabilityBoundingSet = ""; + + DeviceAllow = ""; + + LockPersonality = true; + + PrivateTmp = true; # implied by DynamicUser + PrivateDevices = true; + PrivateUsers = true; + + ProtectClock = true; + ProtectControlGroups = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + + RestrictNamespaces = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; + RestrictRealtime = true; + RestrictSUIDSGID = true; # implied by DynamicUser + + SystemCallArchitectures = "native"; + SystemCallErrorNumber = "EPERM"; + SystemCallFilter = [ + "@system-service" + "~@keyring" + "~@memlock" + "~@privileged" + "~@setuid" + ]; + }; + }; + }; + }; }); } diff --git a/nix/module.nix b/nix/module.nix new file mode 100644 index 0000000..249d697 --- /dev/null +++ b/nix/module.nix @@ -0,0 +1,113 @@ +{ config +, pkgs +, lib +, ... +}: +with lib; let + cfg = config.services.breeze; + settingsFormat = pkgs.formats.toml { }; +in +{ + options = { + services.breeze = { + enable = mkEnableOption "breeze file server"; + + package = mkPackageOption self.packages.${system} "breeze"; + + user = mkOption { + type = types.str; + default = "breeze"; + description = "User that `breeze` will run under"; + }; + + group = mkOption { + type = types.str; + default = "breeze"; + description = "Group that `breeze` will run under"; + }; + + extraGroups = mkOption { + type = types.listOf types.str; + default = [ ]; + description = "Supplementary groups"; + }; + + settings = mkOption { + type = settingsFormat.type; + default = { }; + description = '' + The *.toml configuration to run `breeze` with. + There is no formal documentation, but there is an example in the [readme](https://git.min.rip/min/breeze/src/branch/main/README.md). + ''; + }; + }; + }; + + config = mkIf cfg.enable { + users.users.${cfg.user} = { + isSystemUser = true; + inherit (cfg) group; + }; + + users.groups.${cfg.group} = { }; + + systemd.services.breeze = { + description = "breeze file server"; + after = [ "local-fs.target" "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = rec { + User = cfg.user; + Group = cfg.group; + DynamicUser = false; # we write files, so don't do that + SupplementaryGroups = cfg.extraGroups; + StateDirectory = "breeze"; + CacheDirectory = "breeze"; + ExecStart = escapeShellArgs [ + "${cfg.package}/bin/breeze" + "--config" + (settingsFormat.generate "breeze.toml" cfg.settings) + ]; + Restart = "on-failure"; + + # Security Options # + + NoNewPrivileges = true; # implied by DynamicUser + RemoveIPC = true; # implied by DynamicUser + + AmbientCapabilities = ""; + CapabilityBoundingSet = ""; + + DeviceAllow = ""; + + LockPersonality = true; + + PrivateTmp = true; # implied by DynamicUser + PrivateDevices = true; + PrivateUsers = true; + + ProtectClock = true; + ProtectControlGroups = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + + RestrictNamespaces = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ]; + RestrictRealtime = true; + RestrictSUIDSGID = true; # implied by DynamicUser + + SystemCallArchitectures = "native"; + SystemCallErrorNumber = "EPERM"; + SystemCallFilter = [ + "@system-service" + "~@keyring" + "~@memlock" + "~@privileged" + "~@setuid" + ]; + }; + }; + }; +}