Add dufs fs server to nas
This commit is contained in:
		
							parent
							
								
									72e7e498cc
								
							
						
					
					
						commit
						3849a79fca
					
				@ -15,6 +15,7 @@ in
 | 
				
			|||||||
  fonts.fontconfig.enable = true;
 | 
					  fonts.fontconfig.enable = true;
 | 
				
			||||||
  home.packages = with pkgs; [
 | 
					  home.packages = with pkgs; [
 | 
				
			||||||
    helix
 | 
					    helix
 | 
				
			||||||
 | 
					    ghostty
 | 
				
			||||||
    jq
 | 
					    jq
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #
 | 
					    #
 | 
				
			||||||
 | 
				
			|||||||
@ -14,7 +14,11 @@
 | 
				
			|||||||
  boot.extraModulePackages = [ ];
 | 
					  boot.extraModulePackages = [ ];
 | 
				
			||||||
  boot.supportedFilesystems = [ "zfs" ];
 | 
					  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;
 | 
					  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 = [
 | 
					  imports = [
 | 
				
			||||||
    ./modules/user/main_user.nix
 | 
					    ./modules/user/main_user.nix
 | 
				
			||||||
 | 
					    ./modules/services/dufs.nix
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  config = {
 | 
					  config = {
 | 
				
			||||||
@ -165,9 +166,50 @@ in
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
        auth = {
 | 
					        auth = {
 | 
				
			||||||
          type = "htpasswd";
 | 
					          type = "htpasswd";
 | 
				
			||||||
          htpasswd_filename = "/home/luci/.config/radicale/rad_pass";
 | 
					          htpasswd_filename = "/var/lib/radicale/users";
 | 
				
			||||||
          htpasswd_encryption = "bcrypt";
 | 
					          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