diff --git a/hosts/nixos/hs/system.nix b/hosts/nixos/hs/system.nix index 09e9ac7..d48926a 100644 --- a/hosts/nixos/hs/system.nix +++ b/hosts/nixos/hs/system.nix @@ -7,9 +7,8 @@ ../../../modules/wireguard.nix ../../../modules/podman.nix ../../../modules/traefik.nix - ../../../modules/samba.nix + ../../../modules/nfs.nix ../../../modules/borg-client.nix - ../../../modules/webdav.nix ../../../modules/login-display.nix ]; @@ -197,24 +196,11 @@ - # Samba file sharing configuration - services.samba-custom = { enable = false; }; - - # WebDAV file server configuration - services.webdav-server = { - enable = false; - port = 5009; - servePath = "/mnt/storage/Media"; - auth = { - username = "yanlin"; - passwordFile = "/etc/webdav-password"; - }; - readOnly = false; - allowUpload = true; - allowDelete = true; - allowSearch = true; - allowSymlink = false; - hideDotFiles = true; + # NFS file sharing configuration + services.nfs-custom = { + enable = true; + exportPath = "/mnt/storage/Media"; + allowedNetwork = "10.2.2.0/24"; }; # Login display with SMART disk health status diff --git a/hosts/nixos/thinkpad/system.nix b/hosts/nixos/thinkpad/system.nix index 8dd561d..cfaa893 100644 --- a/hosts/nixos/thinkpad/system.nix +++ b/hosts/nixos/thinkpad/system.nix @@ -5,6 +5,7 @@ ./hardware-configuration.nix ../system-default.nix # Common NixOS system configuration ../../../modules/wireguard.nix + ../../../modules/autofs.nix ../../../modules/login-display.nix ]; @@ -317,4 +318,12 @@ showDiskUsage = true; }; + # AutoFS auto-mounting for remote NFS shares + services.autofs-custom = { + enable = true; + remoteHost = "10.2.2.20"; + remotePath = "/mnt/storage/Media"; + mountPoint = "/mnt/hs-media"; + }; + } diff --git a/modules/autofs.nix b/modules/autofs.nix new file mode 100644 index 0000000..0996aa3 --- /dev/null +++ b/modules/autofs.nix @@ -0,0 +1,44 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.autofs-custom; +in + +{ + options.services.autofs-custom = { + enable = mkEnableOption "AutoFS automatic mounting"; + + remoteHost = mkOption { + type = types.str; + description = "Remote NFS server hostname or IP"; + }; + + remotePath = mkOption { + type = types.str; + description = "Remote path to mount"; + }; + + mountPoint = mkOption { + type = types.str; + description = "Local mount point"; + }; + }; + + config = mkIf cfg.enable { + services.autofs = { + enable = true; + timeout = 300; + autoMaster = '' + ${cfg.mountPoint} -fstype=nfs4,rw,soft,intr,noatime ${cfg.remoteHost}:${cfg.remotePath} + ''; + }; + + systemd.tmpfiles.rules = [ + "d ${cfg.mountPoint} 0755 root root -" + ]; + + environment.systemPackages = [ pkgs.nfs-utils ]; + }; +} diff --git a/modules/nfs.nix b/modules/nfs.nix new file mode 100644 index 0000000..d1af11d --- /dev/null +++ b/modules/nfs.nix @@ -0,0 +1,33 @@ +{ config, lib, ... }: + +with lib; + +let + cfg = config.services.nfs-custom; +in + +{ + options.services.nfs-custom = { + enable = mkEnableOption "NFS server"; + + exportPath = mkOption { + type = types.str; + description = "Path to export via NFS"; + }; + + allowedNetwork = mkOption { + type = types.str; + default = "10.2.2.0/24"; + description = "Network allowed to access the export (CIDR)"; + }; + }; + + config = mkIf cfg.enable { + services.nfs.server = { + enable = true; + exports = '' + ${cfg.exportPath} ${cfg.allowedNetwork}(rw,sync,no_subtree_check,no_root_squash) + ''; + }; + }; +} diff --git a/modules/samba.nix b/modules/samba.nix deleted file mode 100644 index 90343a2..0000000 --- a/modules/samba.nix +++ /dev/null @@ -1,164 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - cfg = config.services.samba-custom; -in - -{ - options.services.samba-custom = { - enable = mkEnableOption "Samba file sharing service"; - - workgroup = mkOption { - type = types.str; - default = "WORKGROUP"; - description = "SMB workgroup name"; - }; - - serverString = mkOption { - type = types.str; - default = "NixOS Samba Server"; - description = "Server description string"; - }; - - shares = mkOption { - type = types.attrsOf (types.submodule { - options = { - path = mkOption { - type = types.str; - description = "Path to the shared directory"; - }; - - comment = mkOption { - type = types.str; - default = ""; - description = "Share description comment"; - }; - - browseable = mkOption { - type = types.bool; - default = true; - description = "Whether share is browseable"; - }; - - readOnly = mkOption { - type = types.bool; - default = false; - description = "Whether share is read-only"; - }; - - guestOk = mkOption { - type = types.bool; - default = false; - description = "Allow guest access"; - }; - - createMask = mkOption { - type = types.str; - default = "0644"; - description = "File creation mask"; - }; - - directoryMask = mkOption { - type = types.str; - default = "0755"; - description = "Directory creation mask"; - }; - - forceUser = mkOption { - type = types.nullOr types.str; - default = null; - description = "Force files to be owned by this user"; - }; - - forceGroup = mkOption { - type = types.nullOr types.str; - default = null; - description = "Force files to be owned by this group"; - }; - - validUsers = mkOption { - type = types.listOf types.str; - default = []; - description = "List of valid users for this share"; - }; - }; - }); - default = {}; - description = "Samba share definitions"; - }; - - enableWSDD = mkOption { - type = types.bool; - default = true; - description = "Enable Web Service Discovery (WSD) for SMB discovery"; - }; - - openFirewall = mkOption { - type = types.bool; - default = false; - description = "Open firewall ports for Samba"; - }; - }; - - config = mkIf cfg.enable { - # Enable Samba service - services.samba = { - enable = true; - - # Enable SMB protocol versions - package = pkgs.samba4Full; - - # Modern Samba configuration using settings - settings = { - global = { - # Server identification - workgroup = cfg.workgroup; - "server string" = cfg.serverString; - - # Security settings - security = "user"; - "map to guest" = "never"; - - # Performance optimizations - "socket options" = "TCP_NODELAY IPTOS_LOWDELAY SO_RCVBUF=524288 SO_SNDBUF=524288"; - deadtime = "30"; - "use sendfile" = "yes"; - - # Logging - "log file" = "/var/log/samba/log.%m"; - "max log size" = "1000"; - "log level" = "0"; - - # Disable printer sharing - "load printers" = "no"; - printing = "bsd"; - "printcap name" = "/dev/null"; - "disable spoolss" = "yes"; - }; - - # Generate share configurations - } // (mapAttrs (name: share: { - path = share.path; - browseable = if share.browseable then "yes" else "no"; - "read only" = if share.readOnly then "yes" else "no"; - "guest ok" = if share.guestOk then "yes" else "no"; - "create mask" = share.createMask; - "directory mask" = share.directoryMask; - "valid users" = concatStringsSep " " share.validUsers; - comment = share.comment; - } // (optionalAttrs (share.forceUser != null) { - "force user" = share.forceUser; - }) // (optionalAttrs (share.forceGroup != null) { - "force group" = share.forceGroup; - })) cfg.shares); - }; - - # Enable SMB discovery - services.samba-wsdd = mkIf cfg.enableWSDD { - enable = true; - openFirewall = cfg.openFirewall; - }; - }; -} diff --git a/modules/webdav.nix b/modules/webdav.nix deleted file mode 100644 index 99182df..0000000 --- a/modules/webdav.nix +++ /dev/null @@ -1,157 +0,0 @@ -{ config, pkgs, lib, ... }: - -with lib; - -let - cfg = config.services.webdav-server; -in -{ - options.services.webdav-server = { - enable = mkEnableOption "WebDAV file server using dufs"; - - port = mkOption { - type = types.port; - default = 5009; - description = "Port to listen on"; - }; - - address = mkOption { - type = types.str; - default = "0.0.0.0"; - description = "Address to bind to"; - }; - - servePath = mkOption { - type = types.str; - description = "Path to serve via WebDAV"; - }; - - auth = mkOption { - type = types.nullOr (types.submodule { - options = { - username = mkOption { - type = types.str; - description = "Username for authentication"; - }; - passwordFile = mkOption { - type = types.str; - description = "Path to file containing password"; - }; - }; - }); - default = null; - description = "Authentication configuration"; - }; - - readOnly = mkOption { - type = types.bool; - default = false; - description = "Make the WebDAV share read-only"; - }; - - allowUpload = mkOption { - type = types.bool; - default = true; - description = "Allow file uploads"; - }; - - allowDelete = mkOption { - type = types.bool; - default = true; - description = "Allow file deletion"; - }; - - allowSearch = mkOption { - type = types.bool; - default = true; - description = "Enable search functionality"; - }; - - allowSymlink = mkOption { - type = types.bool; - default = false; - description = "Allow serving symbolic links"; - }; - - hideDotFiles = mkOption { - type = types.bool; - default = true; - description = "Hide dot files from listing"; - }; - }; - - config = mkIf cfg.enable { - # Install dufs package - environment.systemPackages = [ pkgs.dufs ]; - - # Set password file permissions if auth is enabled - systemd.tmpfiles.rules = mkIf (cfg.auth != null) [ - "z ${cfg.auth.passwordFile} 0640 yanlin users - -" - ]; - - # Create systemd service for dufs - systemd.services.webdav-server = { - description = "WebDAV server using dufs"; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - - serviceConfig = let - permissionArgs = concatStringsSep " " ( - (optional cfg.readOnly "--render-index") ++ - (optional (!cfg.allowUpload) "--no-upload") ++ - (optional (!cfg.allowDelete) "--no-delete") ++ - (optional (!cfg.allowSearch) "--no-search") ++ - (optional cfg.allowSymlink "--allow-symlink") ++ - (optional cfg.hideDotFiles "--hidden '.*, .*/'") - ); - in { - Type = "simple"; - ExecStart = let - startScript = pkgs.writeShellScript "dufs-start" '' - ${if cfg.auth != null then '' - if [ ! -f "${cfg.auth.passwordFile}" ]; then - echo "Error: Password file ${cfg.auth.passwordFile} does not exist" - exit 1 - fi - AUTH_PASSWORD=$(cat ${cfg.auth.passwordFile} | tr -d '\n') - exec ${pkgs.dufs}/bin/dufs \ - --bind ${cfg.address} \ - --port ${toString cfg.port} \ - --auth "${cfg.auth.username}:$AUTH_PASSWORD@/:rw" \ - ${permissionArgs} \ - "${cfg.servePath}" - '' else '' - exec ${pkgs.dufs}/bin/dufs \ - --bind ${cfg.address} \ - --port ${toString cfg.port} \ - ${permissionArgs} \ - "${cfg.servePath}" - ''} - ''; - in "${startScript}"; - Restart = "always"; - RestartSec = "10"; - User = "yanlin"; - Group = "users"; - UMask = "0022"; # Creates dirs as 755, files as 644 - - # Security hardening - PrivateTmp = true; - ProtectSystem = "strict"; - ReadWritePaths = if cfg.readOnly then [] else [ cfg.servePath ]; - ReadOnlyPaths = if cfg.readOnly then [ cfg.servePath ] else []; - NoNewPrivileges = true; - ProtectKernelTunables = true; - ProtectKernelModules = true; - ProtectControlGroups = true; - RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" "AF_NETLINK" ]; # Added AF_UNIX and AF_NETLINK for interface enumeration - RestrictNamespaces = true; - LockPersonality = true; - MemoryDenyWriteExecute = true; - RestrictRealtime = true; - RestrictSUIDSGID = true; - }; - }; - - }; -}