198 lines
6.2 KiB
Nix
198 lines
6.2 KiB
Nix
{ config, pkgs, lib, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
cfg = config.services.naxos.shares.samba;
|
|
in {
|
|
options.services.naxos.shares.samba = {
|
|
enable = mkEnableOption "NaxOS Declarative Samba (SMB) Service";
|
|
|
|
workgroup = mkOption {
|
|
type = types.str;
|
|
default = "WORKGROUP";
|
|
description = "NetBIOS workgroup.";
|
|
};
|
|
|
|
serverString = mkOption {
|
|
type = types.str;
|
|
default = "NaxOS Storage Appliance";
|
|
description = "Server announcement string.";
|
|
};
|
|
|
|
netbiosName = mkOption {
|
|
type = types.str;
|
|
default = "naxos";
|
|
description = "NetBIOS hostname.";
|
|
};
|
|
|
|
allowedHosts = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [ "10.0.0." "192.168." "172.16." "127.0.0.1" "localhost" ];
|
|
description = "Allowed IP subnets or hosts for SMB access.";
|
|
};
|
|
|
|
shares = mkOption {
|
|
type = types.attrsOf (types.submodule {
|
|
options = {
|
|
enable = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = "Whether to publish this share.";
|
|
};
|
|
path = mkOption {
|
|
type = types.str;
|
|
description = "Target local directory or ZFS mountpoint.";
|
|
};
|
|
comment = mkOption {
|
|
type = types.str;
|
|
default = "";
|
|
description = "Share description.";
|
|
};
|
|
readOnly = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Whether the share is read-only.";
|
|
};
|
|
browseable = mkOption {
|
|
type = types.bool;
|
|
default = true;
|
|
description = "Whether the share is visible in network browsing.";
|
|
};
|
|
guestOk = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Whether anonymous/guest access is allowed.";
|
|
};
|
|
validUsers = mkOption {
|
|
type = types.listOf types.str;
|
|
default = [];
|
|
description = "List of valid users or @groups.";
|
|
};
|
|
forceUser = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = "Force UNIX user for all connections.";
|
|
};
|
|
forceGroup = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
description = "Force UNIX group for all connections.";
|
|
};
|
|
createMask = mkOption {
|
|
type = types.str;
|
|
default = "0664";
|
|
description = "File creation permission mask.";
|
|
};
|
|
directoryMask = mkOption {
|
|
type = types.str;
|
|
default = "0775";
|
|
description = "Directory creation permission mask.";
|
|
};
|
|
timeMachine = mkOption {
|
|
type = types.bool;
|
|
default = false;
|
|
description = "Enable Apple Time Machine compatibility mode (vfs_fruit).";
|
|
};
|
|
timeMachineMaxSize = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
example = "512G";
|
|
description = "Quota size limit advertised to Time Machine.";
|
|
};
|
|
};
|
|
});
|
|
default = {};
|
|
description = "Declaratively managed SMB shares.";
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
services.samba = {
|
|
enable = true;
|
|
openFirewall = true;
|
|
settings = {
|
|
global = {
|
|
workgroup = cfg.workgroup;
|
|
"server string" = cfg.serverString;
|
|
"netbios name" = cfg.netbiosName;
|
|
security = "user";
|
|
"hosts allow" = concatStringsSep " " cfg.allowedHosts;
|
|
"hosts deny" = "0.0.0.0/0";
|
|
"guest account" = "nobody";
|
|
"map to guest" = "bad user";
|
|
|
|
# Apple & ZFS ACL compatibility
|
|
"vfs objects" = "catia fruit streams_xattr acl_xattr";
|
|
"fruit:aapl" = "yes";
|
|
"fruit:metadata" = "stream";
|
|
"fruit:model" = "Macmini";
|
|
"fruit:posix_rename" = "yes";
|
|
"fruit:zero_file_id" = "yes";
|
|
|
|
# ACLs and inheritance
|
|
"map acl inherit" = "yes";
|
|
"inherit acls" = "yes";
|
|
"ea support" = "yes";
|
|
};
|
|
} // (mapAttrs' (shareName: shareCfg:
|
|
nameValuePair shareName (
|
|
{
|
|
path = shareCfg.path;
|
|
comment = shareCfg.comment;
|
|
browseable = if shareCfg.browseable then "yes" else "no";
|
|
"read only" = if shareCfg.readOnly then "yes" else "no";
|
|
"guest ok" = if shareCfg.guestOk then "yes" else "no";
|
|
"create mask" = shareCfg.createMask;
|
|
"directory mask" = shareCfg.directoryMask;
|
|
}
|
|
// optionalAttrs (shareCfg.validUsers != []) {
|
|
"valid users" = concatStringsSep " " shareCfg.validUsers;
|
|
}
|
|
// optionalAttrs (shareCfg.forceUser != null) {
|
|
"force user" = shareCfg.forceUser;
|
|
}
|
|
// optionalAttrs (shareCfg.forceGroup != null) {
|
|
"force group" = shareCfg.forceGroup;
|
|
}
|
|
// optionalAttrs shareCfg.timeMachine {
|
|
"vfs objects" = "catia fruit streams_xattr acl_xattr";
|
|
"fruit:time machine" = "yes";
|
|
}
|
|
// optionalAttrs (shareCfg.timeMachine && shareCfg.timeMachineMaxSize != null) {
|
|
"fruit:time machine max size" = shareCfg.timeMachineMaxSize;
|
|
}
|
|
)
|
|
) (filterAttrs (n: v: v.enable) cfg.shares));
|
|
};
|
|
|
|
# Enable Avahi (mDNS / Bonjour) for automatic SMB & Time Machine discovery on macOS/iOS/Windows
|
|
services.avahi = {
|
|
enable = true;
|
|
nssmdns4 = true;
|
|
publish = {
|
|
enable = true;
|
|
userServices = true;
|
|
};
|
|
extraServiceFiles = {
|
|
smb = ''
|
|
<?xml version="1.0" standalone='no'?><!--*-nxml-*-->
|
|
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
|
|
<service-group>
|
|
<name replace-wildcards="yes">%h (NaxOS SMB)</name>
|
|
<service>
|
|
<type>_smb._tcp</type>
|
|
<port>445</port>
|
|
</service>
|
|
<service>
|
|
<type>_device-info._tcp</type>
|
|
<port>0</port>
|
|
<txt-record>model=RackMac</txt-record>
|
|
</service>
|
|
</service-group>
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
}
|