diff --git a/modules/nixos/ldap/hosts.nix b/modules/nixos/ldap/hosts.nix index da2ec6ca..cc25b159 100644 --- a/modules/nixos/ldap/hosts.nix +++ b/modules/nixos/ldap/hosts.nix @@ -77,6 +77,10 @@ in { type = str; default = ""; }; + idViewDnSuffix = mkOption { + type = str; + default = ""; + }; serviceDnSuffix = mkOption { type = str; default = ""; diff --git a/modules/nixos/samba.nix b/modules/nixos/samba.nix index 7da70283..da45fa5d 100644 --- a/modules/nixos/samba.nix +++ b/modules/nixos/samba.nix @@ -1,13 +1,17 @@ { config, lib, + inputs, pkgs, ... }: let + inherit (inputs.self.lib.lib) mkAlmostOptionDefault; inherit (lib.options) mkOption mkEnableOption; - inherit (lib.modules) mkIf mkMerge mkBefore mkForce mkDefault mkOptionDefault; - inherit (lib.attrsets) mapAttrs' mapAttrsToList nameValuePair; - inherit (lib.strings) hasPrefix concatMapStringsSep; + inherit (lib.modules) mkIf mkMerge mkBefore mkAfter mkOptionDefault; + inherit (lib.attrsets) mapAttrs' mapAttrsToList listToAttrs nameValuePair; + inherit (lib.lists) concatLists; + inherit (lib.strings) toUpper hasPrefix concatMapStringsSep concatStringsSep; + inherit (lib.trivial) flip; inherit (config.services) samba-wsdd; cfg = config.services.samba; settingValue = value: @@ -23,6 +27,31 @@ in { settingPrimitive = oneOf [str int bool]; settingType = oneOf [settingPrimitive (listOf settingPrimitive)]; in { + domain = { + netbiosName = mkOption { + type = nullOr str; + default = null; + defaultText = "networking.hostName"; + }; + netbiosName' = mkOption { + type = str; + }; + isWorkgroup = mkOption { + type = bool; + }; + name = mkOption { + type = nullOr str; + default = null; + }; + netbiosHostAddresses = mkOption { + type = attrsOf (listOf str); + default = { }; + }; + lmhosts = mkOption { + type = attrsOf str; + default = { }; + }; + }; ldap = { enable = mkEnableOption "LDAP"; passdb = { @@ -68,6 +97,34 @@ in { default = null; }; }; + tls = { + enable = mkEnableOption "tls" // { + default = cfg.tls.certPath != null; + }; + peer.enable = mkEnableOption "peer verification" // { + default = cfg.tls.caPath != null; + }; + useACMECert = mkOption { + type = nullOr str; + default = null; + }; + certPath = mkOption { + type = nullOr path; + default = null; + }; + keyPath = mkOption { + type = nullOr path; + default = null; + }; + caPath = mkOption { + type = nullOr path; + default = null; + }; + crlPath = mkOption { + type = nullOr path; + default = null; + }; + }; usershare = { enable = mkEnableOption "usershare"; group = mkOption { @@ -154,19 +211,39 @@ in { config = { services.samba = { - package = mkIf cfg.ldap.enable (mkDefault ( + package = mkIf cfg.ldap.enable (mkAlmostOptionDefault ( if cfg.ldap.passdb.enable && cfg.ldap.passdb.backend == "ipasam" then pkgs.samba-ipa else pkgs.samba-ldap )); + domain = { + isWorkgroup = mkOptionDefault (cfg.securityType != "domain" && cfg.securityType != "ads"); + netbiosName' = let + name = if cfg.domain.netbiosName != null then cfg.domain.netbiosName else config.networking.hostName; + in mkOptionDefault (if cfg.domain.isWorkgroup then toUpper name else name); + netbiosHostAddresses = mkIf (cfg.domain.netbiosName != null) { + ${cfg.domain.netbiosName'} = [ "127.0.0.1" "::1" ]; + }; + lmhosts = let + addrs = mapAttrsToList (name: map (flip nameValuePair name)) cfg.domain.netbiosHostAddresses; + in listToAttrs (concatLists addrs); + }; ldap = { - adminPasswordPath = mkIf (cfg.ldap.adminDn != null && hasPrefix "name=anonymous," cfg.ldap.adminDn) (mkDefault ( + adminPasswordPath = mkIf (cfg.ldap.adminDn != null && hasPrefix "name=anonymous," cfg.ldap.adminDn) (mkAlmostOptionDefault ( pkgs.writeText "smb-ldap-anonymous" "anonymous" )); + idmap.domain = mkIf (cfg.domain.name != null) (mkAlmostOptionDefault cfg.domain.name); + }; + tls = let + cert = config.security.acme.certs.${cfg.tls.useACMECert}; + in { + certPath = mkIf (cfg.tls.useACMECert != null) (mkAlmostOptionDefault "${cert.directory}/fullchain.pem"); + keyPath = mkIf (cfg.tls.useACMECert != null) (mkAlmostOptionDefault "${cert.directory}/key.pem"); + caPath = mkIf (cfg.kerberos.enable && config.security.ipa.enable) (mkAlmostOptionDefault "${config.security.ipa.certificate}"); }; idmap.domains = mkMerge [ (mkIf (cfg.ldap.enable && cfg.ldap.idmap.enable) { ldap = { backend = mkOptionDefault "ldap"; - domain = mkDefault cfg.ldap.idmap.domain; + domain = mkAlmostOptionDefault cfg.ldap.idmap.domain; settings = { ldap_url = mkOptionDefault cfg.ldap.url; }; @@ -176,6 +253,10 @@ in { settings = mkMerge ([ { "use sendfile" = mkOptionDefault true; + "mdns name" = mkOptionDefault "mdns"; + "name resolve order" = mkOptionDefault [ "lmhosts" "host" "bcast" ]; + workgroup = mkIf (cfg.domain.name != null) (mkOptionDefault cfg.domain.name); + "netbios name" = mkIf (cfg.domain.netbiosName != null) (mkOptionDefault cfg.domain.netbiosName); } (mkIf (cfg.passdb.smbpasswd.path != null) { "passdb backend" = mkOptionDefault "smbpasswd:${cfg.passdb.smbpasswd.path}"; @@ -194,8 +275,21 @@ in { "dedicated keytab file" = mkIf (cfg.kerberos.keytabPath != null) (mkOptionDefault "FILE:${cfg.kerberos.keytabPath}" ); + "kerberos encryption types" = mkOptionDefault "strong"; "create krb5 conf" = mkOptionDefault false; }) + (mkIf cfg.enableWinbindd { + "winbind nss info" = mkOptionDefault "rfc2307"; + "winbind use default domain" = mkOptionDefault true; + }) + (mkIf cfg.tls.enable { + "tls enabled" = mkOptionDefault true; + "tls verify peer" = mkIf cfg.tls.peer.enable (mkOptionDefault "ca_and_name_if_available"); + "tls certfile" = mkIf (cfg.tls.certPath != null) (mkOptionDefault cfg.tls.certPath); + "tls keyfile" = mkIf (cfg.tls.keyPath != null) (mkOptionDefault cfg.tls.keyPath); + "tls cafile" = mkIf (cfg.tls.caPath != null) (mkOptionDefault cfg.tls.caPath); + "tls crlfile" = mkIf (cfg.tls.crlPath != null) (mkOptionDefault cfg.tls.crlPath); + }) (mkIf cfg.usershare.enable { "usershare allow guests" = mkOptionDefault true; "usershare max shares" = mkOptionDefault 16; @@ -221,6 +315,11 @@ in { "-valid" = false; }; }; + services.samba-wsdd = { + workgroup = mkIf (cfg.domain.name != null && cfg.domain.isWorkgroup) (mkAlmostOptionDefault (toUpper cfg.domain.name)); + domain = mkIf (cfg.domain.name != null && !cfg.domain.isWorkgroup) (mkAlmostOptionDefault cfg.domain.name); + hostname = mkIf (cfg.domain.netbiosName != null) (mkAlmostOptionDefault cfg.domain.netbiosName'); + }; systemd.services.samba-smbd = mkIf cfg.enable { serviceConfig = let @@ -240,6 +339,17 @@ in { "d ${cfg.usershare.path} 1770 root ${cfg.usershare.group}" ]; + networking.hosts = mkIf (cfg.enable && cfg.domain.netbiosName != null) { + "::1" = mkAfter [ cfg.domain.netbiosName' ]; + # not a typo... + "127.0.0.2" = mkAfter [ cfg.domain.netbiosName' ]; + }; + environment.etc."samba/lmhosts" = mkIf (cfg.enable && cfg.domain.lmhosts != { }) { + text = mkMerge ( + mapAttrsToList (address: name: "${address} ${name}") cfg.domain.lmhosts + ); + }; + networking.firewall.interfaces.local = { allowedTCPPorts = mkMerge [ (mkIf (cfg.enable && !cfg.openFirewall) [139 445]) diff --git a/nixos/ipa.nix b/nixos/ipa.nix index 4c1d9e74..25647bed 100644 --- a/nixos/ipa.nix +++ b/nixos/ipa.nix @@ -32,6 +32,7 @@ in { serviceDnSuffix = mkDefault "cn=services,cn=accounts,"; hostDnSuffix = mkDefault "cn=computers,cn=accounts,"; hostGroupDnSuffix = mkDefault "cn=hostgroups,cn=accounts,"; + idViewDnSuffix = mkDefault "cn=views,cn=accounts,"; sysAccountDnSuffix = mkDefault "cn=sysaccounts,cn=etc,"; domainDnSuffix = mkDefault "cn=ad,cn=etc,"; }; diff --git a/nixos/samba.nix b/nixos/samba.nix index a96fa5d4..f08f937f 100644 --- a/nixos/samba.nix +++ b/nixos/samba.nix @@ -1,27 +1,41 @@ { inputs, config, + access, + system, lib, ... }: let inherit (inputs.self.lib.lib) mkBaseDn; inherit (lib.modules) mkIf mkMerge mkDefault; - inherit (lib.lists) any; - inherit (lib.strings) toUpper hasInfix; + inherit (lib.strings) toUpper removeSuffix; cfg = config.services.samba; inherit (config.networking) domain; - hasIpv4 = any (hasInfix ".") config.systemd.network.networks.eth0.address or []; + inherit (config.users) ldap; + debugLogging = false; + ldapReadOnly = true; in { services.samba = { enable = mkDefault true; - enableWinbindd = mkDefault false; - enableNmbd = mkDefault hasIpv4; + enableWinbindd = mkDefault true; + enableNmbd = mkDefault true; securityType = mkDefault "user"; + # TODO: securityType = "ADS"? kerberos..! + domain = { + name = "GENSOKYO"; + netbiosName = "reisen"; + netbiosHostAddresses = { + ${cfg.domain.netbiosName'} = mkIf system.network.networks.local.enable or false [ + system.network.networks.local.address4 + system.network.networks.local.address6 + ]; + }; + }; ldap = { enable = mkDefault true; url = mkDefault "ldaps://ldap.int.${domain}"; baseDn = mkDefault (mkBaseDn domain); - adminDn = mkDefault "uid=samba,cn=sysaccounts,cn=etc,${cfg.ldap.baseDn}"; + adminDn = mkDefault "uid=samba,${ldap.sysAccountDnSuffix}${cfg.ldap.baseDn}"; adminPasswordPath = mkIf cfg.ldap.enable ( mkDefault config.sops.secrets.smb-ldap-password.path ); @@ -30,8 +44,7 @@ in { #backend = mkIf config.security.ipa.enable (mkDefault "ipasam"); }; idmap = { - enable = mkIf config.services.sssd.enable (mkDefault false); - domain = mkDefault cfg.settings.workgroup; + #enable = mkIf config.services.sssd.enable (mkDefault false); }; }; kerberos = mkIf (config.security.krb5.enable || config.security.ipa.enable) { @@ -49,40 +62,78 @@ in { mkDefault config.sops.secrets.smbpasswd.path ); settings = mkMerge [ { - workgroup = "GENSOKYO"; - "local master" = false; - "preferred master" = false; + "local master" = true; + "preferred master" = true; "winbind offline logon" = true; "winbind scan trusted domains" = false; "winbind use default domain" = true; - "domain master" = false; + "domain master" = true; + "server role" = "classic primary domain controller"; "domain logons" = true; - "remote announce" = mkIf hasIpv4 [ - "10.1.1.255/${cfg.settings.workgroup}" + "remote announce" = [ + "10.1.1.255/${cfg.domain.name}" + ]; + "additional dns hostnames" = mkMerge [ + [ + config.networking.fqdn + "smb.${domain}" + ] + (mkIf system.network.networks.local.enable or false [ + "smb.local.${domain}" + access.hostnameForNetwork.local + ]) + (mkIf system.network.networks.int.enable or false [ + "smb.int.${domain}" + access.hostnameForNetwork.int + ]) + (mkIf config.services.tailscale.enable [ + "smb.tail.${domain}" + access.hostnameForNetwork.tail + ]) ]; } (mkIf cfg.ldap.enable { "ldapsam:trusted" = true; "ldapsam:editposix" = false; - "ldap user suffix" = "cn=users,cn=accounts"; - "ldap group suffix" = "cn=groups,cn=accounts"; + "ldap user suffix" = removeSuffix "," ldap.userDnSuffix; + "ldap group suffix" = removeSuffix "," ldap.groupDnSuffix; + "ldap machine suffix" = removeSuffix "," ldap.hostDnSuffix; + "ldap idmap suffix" = removeSuffix "," ldap.idViewDnSuffix; + "ldap server require strong auth" = "allow_sasl_over_tls"; + # TODO: ldap delete dn? + # TODO: username map script? + }) (mkIf debugLogging { + "ldap debug level" = 1; + #"ldap debug threshold" = 3; # 4? 5? + logging = "systemd"; + "log level" = [ + "4" + #"passdb:8" + #"auth:8" + #"idmap:8" + #"winbind:6" + #"dns:8" + ]; }) ]; idmap.domains = { nss = mkIf (!cfg.ldap.enable || !cfg.ldap.idmap.enable) { backend = "nss"; domain = "*"; range.min = 8000; - #range.max = 8256; + #range.max = 9000; + range.max = 65535; }; ldap = mkIf (cfg.ldap.enable && cfg.ldap.idmap.enable) { range.min = 8000; - #range.min = 8256; + #range.max = 9000; + range.max = 65535; + readOnly = ldapReadOnly; }; }; }; services.samba-wsdd = { enable = mkIf cfg.enable (mkDefault true); - hostname = mkDefault config.networking.hostName; + interface = mkDefault config.systemd.network.networks.eth0.name; }; sops.secrets = { diff --git a/systems/hakurei/nixos.nix b/systems/hakurei/nixos.nix index f3cb8b7a..e5fdd278 100644 --- a/systems/hakurei/nixos.nix +++ b/systems/hakurei/nixos.nix @@ -98,6 +98,14 @@ in { (mkIf config.services.tailscale.enable "mqtt.tail.${config.networking.domain}") ]; }; + samba = { + domain = "smb.${config.networking.domain}"; + extraDomainNames = [ + "smb.local.${config.networking.domain}" + "smb.int.${config.networking.domain}" + (mkIf config.services.tailscale.enable "smb.tail.${config.networking.domain}") + ]; + }; sso = { inherit (nginx) group; domain = virtualHosts.keycloak.serverName; @@ -307,6 +315,9 @@ in { }; }; }; + services.samba.tls = { + useACMECert = "samba"; + }; services.tailscale.advertiseExitNode = true;