{ lib, ... }: { _class = "clan.service"; manifest.name = "samba"; manifest.description = "Samba configuration for NAS"; manifest.readme = "Samba configuration for NAS"; manifest.categories = [ "System" ]; roles.server = { description = "A server role that host files"; interface = { lib, ... }: let userOptions = { readPerm = lib.mkOption { type = with lib.types; bool; description = "Permission to read"; default = false; }; writePerm = lib.mkOption { type = with lib.types; bool; description = "Permission to write"; default = false; }; }; in { options = { globalUsers = lib.mkOption { type = with lib.types; attrsOf (submodule { options = userOptions; }); description = "List of global users with permissions, this will be applied to all the folders."; default = { admin = { readPerm = true; writePerm = true; }; }; }; sharedFolders = lib.mkOption { type = with lib.types; attrsOf (submodule { options = { users = lib.mkOption { type = with lib.types; attrsOf (submodule { options = userOptions; }); description = "List of users with permissions, this will only applied to this particular folder."; default = { }; }; allowedGuest = lib.mkOption { type = with lib.types; bool; description = "Whether to allow guest access to this folder."; default = false; }; }; }); description = "List of folders with users permissions."; default = [ { name = "DEFAULT"; } ]; }; dataDir = lib.mkOption { type = with lib.types; oneOf [ str path ]; description = "A directory where all samba folders will be."; }; }; }; perInstance = { settings, ... }: let allUsernameList = lib.uniqueStrings ( lib.flatten ( (lib.mapAttrsToList (name: share: builtins.attrNames share.users) settings.sharedFolders) ++ builtins.attrNames settings.globalUsers ) ); in { nixosModule = { lib, config, pkgs, ... }: { users.users = builtins.listToAttrs ( map ( username: lib.nameValuePair username { isSystemUser = true; group = username; } ) (allUsernameList ++ builtins.attrNames settings.sharedFolders) ); users.groups = builtins.listToAttrs ( map (username: lib.nameValuePair username { }) ( allUsernameList ++ builtins.attrNames settings.sharedFolders ) ); clan.core.vars.generators = builtins.listToAttrs ( map ( username: lib.nameValuePair "${username}-smb-password" { files.password = { }; runtimeInputs = with pkgs; [ coreutils xkcdpass mkpasswd ]; script = '' xkcdpass --numwords 3 --delimiter - --count 1 > $out/password ''; } ) allUsernameList ); systemd.services.samba-smbd.postStart = lib.concatMapStrings ( user: let passwordPath = config.clan.core.vars.generators."${user}-smb-password".files.password.path; userDir = "${settings.dataDir}/${user}"; in '' mkdir -p ${userDir} chown ${user}:users ${userDir} # if a password is unchanged, this will error (echo $(<${passwordPath}); echo $(<${passwordPath})) | ${config.services.samba.package}/bin/smbpasswd -s -a ${user} '' ) allUsernameList + lib.concatMapStrings ( share: let shareDir = "${settings.dataDir}/${share}"; in '' mkdir -p ${shareDir} chown ${share}:${share} ${shareDir} '' ) (builtins.attrNames settings.sharedFolders); services.samba = { enable = true; openFirewall = true; settings = { global = { security = "user"; workgroup = "WORKGROUP"; "server string" = "WhiteHouse NAS"; "max log size" = "50"; "dns proxy" = false; "syslog only" = true; "map to guest" = "Bad User"; "guest account" = "nobody"; }; } // lib.mapAttrs ( shareName: value: { path = "${settings.dataDir}/${shareName}"; comment = shareName; "force user" = shareName; "force group" = shareName; "create mask" = "0640"; "directory mask" = "0750"; "read only" = "yes"; browseable = "yes"; printable = "no"; "read list" = lib.concatStringsSep " " ( lib.uniqueStrings ( lib.flatten ( (builtins.attrNames (lib.filterAttrs (n: v: v.readPerm) value.users)) ++ (builtins.attrNames (lib.filterAttrs (n: v: v.readPerm) settings.globalUsers)) ) ) ); "write list" = lib.concatStringsSep " " ( lib.uniqueStrings ( lib.flatten ( (builtins.attrNames (lib.filterAttrs (n: v: v.writePerm) value.users)) ++ (builtins.attrNames (lib.filterAttrs (n: v: v.writePerm) settings.globalUsers)) ) ) ); } // lib.optionalAttrs (value.allowedGuest) { public = "yes"; "guest ok" = "yes"; } ) settings.sharedFolders // builtins.listToAttrs ( map ( user: lib.nameValuePair user { comment = user; path = "${settings.dataDir}/${user}"; "force user" = user; "force group" = "users"; public = "yes"; "guest ok" = "no"; "create mask" = "0640"; "directory mask" = "0750"; writable = "yes"; browseable = "yes"; printable = "no"; "valid users" = user; } ) allUsernameList ); }; services.samba-wsdd = { enable = true; openFirewall = true; }; services.avahi = { publish.enable = true; publish.userServices = true; # ^^ Needed to allow samba to automatically register mDNS records (without the need for an `extraServiceFile` nssmdns4 = true; # ^^ Not one hundred percent sure if this is needed- if it aint broke, don't fix it enable = true; openFirewall = true; }; }; }; }; }