Files
Lukas Holzner 5da58ae6d7
Test NaxOS Module Configurations / test-modules (push) Failing after 6m10s
feat(core): initialize NaxOS declarative NixOS distribution, modules, and ISO installer
2026-09-03 23:53:18 +02:00

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