add nfs and autofs system

This commit is contained in:
Yan Lin 2025-10-25 15:30:08 +02:00
parent 27408723d9
commit 73b137dc84
6 changed files with 92 additions and 341 deletions

44
modules/autofs.nix Normal file
View file

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

33
modules/nfs.nix Normal file
View file

@ -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)
'';
};
};
}

View file

@ -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;
};
};
}

View file

@ -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;
};
};
};
}