# 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]; }