Add dufs fs server to nas
This commit is contained in:
		
							parent
							
								
									72e7e498cc
								
							
						
					
					
						commit
						3849a79fca
					
				@ -15,6 +15,7 @@ in
 | 
			
		||||
  fonts.fontconfig.enable = true;
 | 
			
		||||
  home.packages = with pkgs; [
 | 
			
		||||
    helix
 | 
			
		||||
    ghostty
 | 
			
		||||
    jq
 | 
			
		||||
 | 
			
		||||
    #
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,11 @@
 | 
			
		||||
  boot.extraModulePackages = [ ];
 | 
			
		||||
  boot.supportedFilesystems = [ "zfs" ];
 | 
			
		||||
 | 
			
		||||
  boot.kernelPackages = pkgs.linuxPackages_6_10;
 | 
			
		||||
  boot.kernelPackages = pkgs.linuxPackages; # latest LTS - should be safe with zfs
 | 
			
		||||
    # List all available kernel packages
 | 
			
		||||
    # nix-shell -p nixpkgs-unstable --run "nix repl"
 | 
			
		||||
    # nix-repl> :l <nixpkgs>
 | 
			
		||||
    # nix-repl> pkgs.linuxPackages<TAB>
 | 
			
		||||
 | 
			
		||||
  boot.zfs.forceImportRoot = false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										208
									
								
								shared/modules/services/dufs.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								shared/modules/services/dufs.nix
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,208 @@
 | 
			
		||||
{ config, lib, pkgs, ... }:
 | 
			
		||||
 | 
			
		||||
let
 | 
			
		||||
  cfg = config.services.dufs;
 | 
			
		||||
 | 
			
		||||
  # Helper to create auth string from user list
 | 
			
		||||
  mkAuthString = users: lib.concatMapStringsSep "|"
 | 
			
		||||
    (user: "${user.username}:${user.passwordHash}@/:rw")
 | 
			
		||||
    users;
 | 
			
		||||
in
 | 
			
		||||
{
 | 
			
		||||
  options.services.dufs = {
 | 
			
		||||
    enable = lib.mkEnableOption "dufs file server";
 | 
			
		||||
 | 
			
		||||
    package = lib.mkOption {
 | 
			
		||||
      type = lib.types.package;
 | 
			
		||||
      default = pkgs.dufs;
 | 
			
		||||
      description = "The dufs package to use";
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    servePathPublic = lib.mkOption {
 | 
			
		||||
      type = lib.types.str;
 | 
			
		||||
      default = "/nfs_export/kage/dufs/public";
 | 
			
		||||
      description = "Directory path to serve files from";
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    servePathPrivate = lib.mkOption {
 | 
			
		||||
      type = lib.types.str;
 | 
			
		||||
      default = "/nfs_export/kage/dufs/private";
 | 
			
		||||
      description = "Directory path to serve files from";
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    user = lib.mkOption {
 | 
			
		||||
      type = lib.types.str;
 | 
			
		||||
      default = "dufs";
 | 
			
		||||
      description = "User to run dufs service as";
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    group = lib.mkOption {
 | 
			
		||||
      type = lib.types.str;
 | 
			
		||||
      default = "dufs";
 | 
			
		||||
      description = "Group to run dufs service as";
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    publicInstance = {
 | 
			
		||||
      enable = lib.mkEnableOption "public read-only instance";
 | 
			
		||||
 | 
			
		||||
      port = lib.mkOption {
 | 
			
		||||
        type = lib.types.port;
 | 
			
		||||
        default = 5000;
 | 
			
		||||
        description = "Port for public read-only instance";
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      bind = lib.mkOption {
 | 
			
		||||
        type = lib.types.str;
 | 
			
		||||
        default = "0.0.0.0";
 | 
			
		||||
        description = "Bind address for public instance";
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      allowSearch = lib.mkOption {
 | 
			
		||||
        type = lib.types.bool;
 | 
			
		||||
        default = true;
 | 
			
		||||
        description = "Allow searching in public instance";
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    privateInstance = {
 | 
			
		||||
      enable = lib.mkEnableOption "private read-write instance";
 | 
			
		||||
 | 
			
		||||
      port = lib.mkOption {
 | 
			
		||||
        type = lib.types.port;
 | 
			
		||||
        default = 5001;
 | 
			
		||||
        description = "Port for private authenticated instance";
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      bind = lib.mkOption {
 | 
			
		||||
        type = lib.types.str;
 | 
			
		||||
        default = "0.0.0.0";
 | 
			
		||||
        description = "Bind address for private instance";
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      users = lib.mkOption {
 | 
			
		||||
        type = lib.types.listOf (lib.types.submodule {
 | 
			
		||||
          options = {
 | 
			
		||||
            username = lib.mkOption {
 | 
			
		||||
              type = lib.types.str;
 | 
			
		||||
              description = "Username for authentication";
 | 
			
		||||
            };
 | 
			
		||||
            passwordHash = lib.mkOption {
 | 
			
		||||
              type = lib.types.str;
 | 
			
		||||
              description = "SHA-512 password hash (generate with: openssl passwd -6)";
 | 
			
		||||
            };
 | 
			
		||||
          };
 | 
			
		||||
        });
 | 
			
		||||
        default = [];
 | 
			
		||||
        description = "List of users with read-write access";
 | 
			
		||||
        example = [
 | 
			
		||||
          {
 | 
			
		||||
            username = "admin";
 | 
			
		||||
            passwordHash = "$6$rounds=656000$...";
 | 
			
		||||
          }
 | 
			
		||||
        ];
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      allowUpload = lib.mkOption {
 | 
			
		||||
        type = lib.types.bool;
 | 
			
		||||
        default = true;
 | 
			
		||||
        description = "Allow file uploads";
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      allowDelete = lib.mkOption {
 | 
			
		||||
        type = lib.types.bool;
 | 
			
		||||
        default = true;
 | 
			
		||||
        description = "Allow file deletion";
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      allowSearch = lib.mkOption {
 | 
			
		||||
        type = lib.types.bool;
 | 
			
		||||
        default = true;
 | 
			
		||||
        description = "Allow searching";
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    openFirewall = lib.mkOption {
 | 
			
		||||
      type = lib.types.bool;
 | 
			
		||||
      default = false;
 | 
			
		||||
      description = "Open firewall ports for enabled instances";
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  config = lib.mkIf cfg.enable {
 | 
			
		||||
    # Create dufs user and group
 | 
			
		||||
    users.users.${cfg.user} = {
 | 
			
		||||
      isSystemUser = true;
 | 
			
		||||
      group = cfg.group;
 | 
			
		||||
      description = "dufs file server user";
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    users.groups.${cfg.group} = {};
 | 
			
		||||
 | 
			
		||||
    # Public read-only instance
 | 
			
		||||
    systemd.services.dufs-public = lib.mkIf cfg.publicInstance.enable {
 | 
			
		||||
      description = "dufs public read-only file server";
 | 
			
		||||
      wantedBy = [ "multi-user.target" ];
 | 
			
		||||
      after = [ "network.target" ];
 | 
			
		||||
 | 
			
		||||
      serviceConfig = {
 | 
			
		||||
        Type = "simple";
 | 
			
		||||
        User = cfg.user;
 | 
			
		||||
        Group = cfg.group;
 | 
			
		||||
        ExecStart = ''
 | 
			
		||||
          ${cfg.package}/bin/dufs ${cfg.servePathPublic} \
 | 
			
		||||
            --bind ${cfg.publicInstance.bind} \
 | 
			
		||||
            --port ${toString cfg.publicInstance.port} \
 | 
			
		||||
            ${lib.optionalString cfg.publicInstance.allowSearch "--allow-search"} \
 | 
			
		||||
            --render-index
 | 
			
		||||
        '';
 | 
			
		||||
        Restart = "on-failure";
 | 
			
		||||
        RestartSec = "10s";
 | 
			
		||||
 | 
			
		||||
        # Hardening
 | 
			
		||||
        NoNewPrivileges = true;
 | 
			
		||||
        PrivateTmp = true;
 | 
			
		||||
        ProtectSystem = "strict";
 | 
			
		||||
        ProtectHome = true;
 | 
			
		||||
        ReadOnlyPaths = [ cfg.servePathPublic ];
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    # Private read-write instance
 | 
			
		||||
    systemd.services.dufs-private = lib.mkIf cfg.privateInstance.enable {
 | 
			
		||||
      description = "dufs private read-write file server";
 | 
			
		||||
      wantedBy = [ "multi-user.target" ];
 | 
			
		||||
      after = [ "network.target" ];
 | 
			
		||||
 | 
			
		||||
      serviceConfig = {
 | 
			
		||||
        Type = "simple";
 | 
			
		||||
        User = cfg.user;
 | 
			
		||||
        Group = cfg.group;
 | 
			
		||||
        ExecStart = ''
 | 
			
		||||
          ${cfg.package}/bin/dufs ${cfg.servePathPrivate} \
 | 
			
		||||
            --bind ${cfg.privateInstance.bind} \
 | 
			
		||||
            --port ${toString cfg.privateInstance.port} \
 | 
			
		||||
            --auth '${mkAuthString cfg.privateInstance.users}' \
 | 
			
		||||
            ${lib.optionalString cfg.privateInstance.allowUpload "--allow-upload"} \
 | 
			
		||||
            ${lib.optionalString cfg.privateInstance.allowDelete "--allow-delete"} \
 | 
			
		||||
            ${lib.optionalString cfg.privateInstance.allowSearch "--allow-search"} \
 | 
			
		||||
            --render-index
 | 
			
		||||
        '';
 | 
			
		||||
        Restart = "on-failure";
 | 
			
		||||
        RestartSec = "10s";
 | 
			
		||||
 | 
			
		||||
        # Hardening
 | 
			
		||||
        NoNewPrivileges = true;
 | 
			
		||||
        PrivateTmp = true;
 | 
			
		||||
        ProtectSystem = "strict";
 | 
			
		||||
        ProtectHome = true;
 | 
			
		||||
        ReadWritePaths = [ cfg.servePathPrivate ];
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    # Firewall configuration
 | 
			
		||||
    networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall (
 | 
			
		||||
      (lib.optional cfg.publicInstance.enable cfg.publicInstance.port) ++
 | 
			
		||||
      (lib.optional cfg.privateInstance.enable cfg.privateInstance.port)
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
@ -55,6 +55,7 @@ in
 | 
			
		||||
 | 
			
		||||
  imports = [
 | 
			
		||||
    ./modules/user/main_user.nix
 | 
			
		||||
    ./modules/services/dufs.nix
 | 
			
		||||
  ];
 | 
			
		||||
  
 | 
			
		||||
  config = {
 | 
			
		||||
@ -165,9 +166,50 @@ in
 | 
			
		||||
        };
 | 
			
		||||
        auth = {
 | 
			
		||||
          type = "htpasswd";
 | 
			
		||||
          htpasswd_filename = "/home/luci/.config/radicale/rad_pass";
 | 
			
		||||
          htpasswd_filename = "/var/lib/radicale/users";
 | 
			
		||||
          htpasswd_encryption = "bcrypt";
 | 
			
		||||
        };
 | 
			
		||||
        storage = {
 | 
			
		||||
          filesystem_folder = "/var/lib/radicale/collections";
 | 
			
		||||
        };
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    # DUFS file server configuration
 | 
			
		||||
    services.dufs = {
 | 
			
		||||
      enable = true;
 | 
			
		||||
      openFirewall = true;
 | 
			
		||||
 | 
			
		||||
      # Public read-only instance
 | 
			
		||||
      publicInstance = {
 | 
			
		||||
        enable = true;
 | 
			
		||||
        port = 5000;
 | 
			
		||||
        allowSearch = true;
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      # Private read-write instance with authentication
 | 
			
		||||
      privateInstance = {
 | 
			
		||||
        enable = true;
 | 
			
		||||
        port = 5001;
 | 
			
		||||
        allowUpload = true;
 | 
			
		||||
        allowDelete = true;
 | 
			
		||||
        allowSearch = true;
 | 
			
		||||
        users = [
 | 
			
		||||
          # Generate password hash with: openssl passwd -6
 | 
			
		||||
          # Replace with actual username and hash
 | 
			
		||||
          # {
 | 
			
		||||
          #   username = "admin";
 | 
			
		||||
          #   passwordHash = "$6$rounds=656000$...";
 | 
			
		||||
          # }
 | 
			
		||||
          {
 | 
			
		||||
            username = "nate";
 | 
			
		||||
            passwordHash = "$6$rounds=656000$6$3.mottgY50yEZJlr$8a8ztrB/G2kZ39C0cAMDEfQGd93sqL4tS.gQKjnDrRQVvE.VTIlp5JF/GRW95YsKhaOF3r9ui9RTj88Z8LBV80";
 | 
			
		||||
          }
 | 
			
		||||
          {
 | 
			
		||||
            username = "guest";
 | 
			
		||||
            passwordHash = "$6$rounds=656000$6$tJlnxZhnNFPaDEXf$0Q.wZwDczaLfk5rIvX6FfCYvS75IY16WpuXKJyRMbdq4Ie8mZC3fSp5oOB95bMDRHRcabexi5Fp8j39c0pYc8.";
 | 
			
		||||
          }
 | 
			
		||||
        ];
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user