From e62691fbda4f645b66469240502b6cf5b295d340 Mon Sep 17 00:00:00 2001 From: Fabian Montero Date: Thu, 28 Aug 2025 18:34:17 -0600 Subject: [PATCH] vps: add dufs --- sys/modules/default.nix | 1 + sys/modules/dufs.nix | 233 ++++++++++++++++++++++++++++++ sys/platforms/vps/srv/default.nix | 1 + sys/platforms/vps/srv/dufs.nix | 32 ++++ 4 files changed, 267 insertions(+) create mode 100644 sys/modules/dufs.nix create mode 100644 sys/platforms/vps/srv/dufs.nix diff --git a/sys/modules/default.nix b/sys/modules/default.nix index bb54ba6..ed2f200 100644 --- a/sys/modules/default.nix +++ b/sys/modules/default.nix @@ -17,5 +17,6 @@ ./steam.nix ./gtklock.nix ./borgsync.nix + ./dufs.nix ]; } diff --git a/sys/modules/dufs.nix b/sys/modules/dufs.nix new file mode 100644 index 0000000..8dab7b4 --- /dev/null +++ b/sys/modules/dufs.nix @@ -0,0 +1,233 @@ +# https://github.com/NixOS/nixpkgs/blob/c77cd68706b590b44334bb8c506239b3384c26a0/nixos/modules/services/misc/dufs.nix +{ + config, + lib, + pkgs, + ... +}: +with lib; let + cfg = config.local.sys.dufs; + types = lib.types; +in { + options.local.sys.dufs = { + enable = lib.mkEnableOption "the dufs server"; + package = lib.mkPackageOption pkgs "dufs" {}; + settings = lib.mkOption { + type = types.submodule { + options = { + serve-path = lib.mkOption { + type = types.path; + description = "Specific path to serve."; + }; + bind = lib.mkOption { + type = types.nullOr types.str; + description = "Specify bind address or unix socket."; + default = null; + }; + port = lib.mkOption { + type = types.port; + description = "Specify port to listen on."; + default = 5000; + }; + path-prefix = lib.mkOption { + type = types.nullOr types.path; + description = "Specify a path prefix."; + default = null; + }; + hidden = lib.mkOption { + type = types.listOf types.str; + description = "Hide paths from directory listings, e.g. tmp,*.log,*.lock."; + default = []; + example = lib.literalExpression '' + [ + "tmp" + "*.log" + "*.lock." + ] + ''; + }; + allow-all = lib.mkOption { + type = types.bool; + description = "Allow all operations."; + default = true; + }; + allow-upload = lib.mkOption { + type = types.bool; + description = "Allow upload files/folders."; + default = false; + }; + allow-delete = lib.mkOption { + type = types.bool; + description = "Allow delete files/folders."; + default = false; + }; + allow-search = lib.mkOption { + type = types.bool; + description = "Allow search files/folders."; + default = false; + }; + allow-symlink = lib.mkOption { + type = types.bool; + description = "Allow symlink to files/folders outside root directory."; + default = false; + }; + allow-archive = lib.mkOption { + type = types.bool; + description = "Allow zip archive generation."; + default = false; + }; + enable-cors = lib.mkOption { + type = types.bool; + description = "Enable CORS, sets `Access-Control-Allow-Origin: *`."; + default = false; + }; + render-index = lib.mkOption { + type = types.bool; + description = "Serve index.html when requesting a directory, returns 404 if not found index.html."; + default = false; + }; + render-try-index = lib.mkOption { + type = types.bool; + description = "Serve index.html when requesting a directory, returns directory listing if not found index.html."; + default = false; + }; + render-spa = lib.mkOption { + type = types.bool; + description = "Serve SPA(Single Page Application)."; + default = false; + }; + assets = lib.mkOption { + type = types.nullOr types.path; + description = "Set the path to the assets directory for overriding the built-in assets."; + default = null; + }; + log-format = lib.mkOption { + type = types.nullOr types.str; + description = "Customize http log format."; + default = null; + example = lib.literalExpression '' + "$remote_addr \"$request\" $status" + ''; + }; + compress = lib.mkOption { + type = types.enum [ + "none" + "low" + "medium" + "high" + ]; + description = "Customize http log format."; + default = "none"; + }; + tls-cert = lib.mkOption { + type = types.nullOr types.path; + description = "Path to an SSL/TLS certificate to serve with HTTPS."; + default = null; + }; + tls-key = lib.mkOption { + type = types.nullOr types.path; + description = "Path to the SSL/TLS certificate's private key."; + default = null; + }; + }; + }; + description = "Settings for dufs."; + }; + authFile = lib.mkOption { + type = types.nullOr types.path; + description = '' + Path to file containing auth roles (e.g. user:pass@/dir1:rw,/dir2), one per line. + + Passwords may be hashed, see https://github.com/sigoden/dufs#hashed-password. + ''; + default = null; + }; + openFirewall = lib.mkOption { + type = types.bool; + description = "Open firewall on configured port."; + default = false; + }; + user = lib.mkOption { + type = types.str; + description = "User to run dufs under."; + default = "dufs"; + }; + group = lib.mkOption { + type = types.str; + description = "Group to run dufs under."; + default = "dufs"; + }; + }; + config = lib.mkIf cfg.enable { + networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [cfg.settings.port]; + systemd.services.dufs = let + settings = lib.filterAttrs (_: v: v != null) cfg.settings; + pathWritable = settings.allow-all || settings.allow-upload || settings.allow-delete; + in { + after = ["network.target"]; + wantedBy = ["multi-user.target"]; + environment.DUFS_CONFIG = (pkgs.formats.yaml {}).generate "dufs-config.yaml" settings; + script = '' + ${lib.optionalString (cfg.authFile != null) '' + export DUFS_AUTH=$(tr '\n' '|' < ${lib.escapeShellArg cfg.authFile} | sed 's/|$//') + ''} + exec ${lib.escapeShellArg (lib.getExe cfg.package)} + ''; + serviceConfig = { + BindReadOnlyPaths = + [ + builtins.storeDir + ] + ++ lib.optional (!pathWritable) settings.serve-path + ++ lib.optional (cfg.authFile != null) cfg.authFile; + BindPaths = lib.mkIf pathWritable settings.serve-path; + CapabilityBoundingSet = ""; + DeviceAllow = ""; + Group = cfg.group; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = [ + "AF_INET" + "AF_INET6" + "AF_NETLINK" + ]; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + RootDirectory = "/run/dufs"; + RuntimeDirectory = "dufs"; + SystemCallArchitectures = "native"; + SystemCallFilter = [ + "@system-service" + "~@resources" + "~@privileged" + ]; + User = cfg.user; + }; + }; + users = { + users.dufs = lib.mkIf (cfg.user == "dufs") { + group = cfg.group; + home = cfg.settings.serve-path; + isSystemUser = true; + }; + groups.dufs = lib.mkIf (cfg.group == "dufs") {}; + }; + }; + meta.maintainers = with lib.maintainers; [jackwilsdon]; +} diff --git a/sys/platforms/vps/srv/default.nix b/sys/platforms/vps/srv/default.nix index f4cb031..71beeca 100644 --- a/sys/platforms/vps/srv/default.nix +++ b/sys/platforms/vps/srv/default.nix @@ -17,5 +17,6 @@ with lib; { ./calibre-web.nix ./immich.nix ./mealie.nix + ./dufs.nix ]; } diff --git a/sys/platforms/vps/srv/dufs.nix b/sys/platforms/vps/srv/dufs.nix new file mode 100644 index 0000000..da38169 --- /dev/null +++ b/sys/platforms/vps/srv/dufs.nix @@ -0,0 +1,32 @@ +{ + lib, + pkgs, + config, + ... +}: +with lib; { + services = { + nginx = { + virtualHosts."public.posixlycorrect.com" = { + enableACME = true; + forceSSL = true; + extraConfig = '' + proxy_headers_hash_max_size 512; + proxy_headers_hash_bucket_size 128; + ''; + locations."/" = { + proxyPass = "http://127.0.0.1:5000"; + }; + }; + }; + }; + + local.sys.dufs = { + enable = true; + settings = { + serve-path = "/var/public"; + allow-all = false; + allow-archive = true; + }; + }; +}