feat(samba): tls

This commit is contained in:
arcnmx 2024-04-03 08:29:46 -07:00
parent 34d1b400e1
commit fbf96aacef
5 changed files with 202 additions and 25 deletions

View file

@ -77,6 +77,10 @@ in {
type = str; type = str;
default = ""; default = "";
}; };
idViewDnSuffix = mkOption {
type = str;
default = "";
};
serviceDnSuffix = mkOption { serviceDnSuffix = mkOption {
type = str; type = str;
default = ""; default = "";

View file

@ -1,13 +1,17 @@
{ {
config, config,
lib, lib,
inputs,
pkgs, pkgs,
... ...
}: let }: let
inherit (inputs.self.lib.lib) mkAlmostOptionDefault;
inherit (lib.options) mkOption mkEnableOption; inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkBefore mkForce mkDefault mkOptionDefault; inherit (lib.modules) mkIf mkMerge mkBefore mkAfter mkOptionDefault;
inherit (lib.attrsets) mapAttrs' mapAttrsToList nameValuePair; inherit (lib.attrsets) mapAttrs' mapAttrsToList listToAttrs nameValuePair;
inherit (lib.strings) hasPrefix concatMapStringsSep; inherit (lib.lists) concatLists;
inherit (lib.strings) toUpper hasPrefix concatMapStringsSep concatStringsSep;
inherit (lib.trivial) flip;
inherit (config.services) samba-wsdd; inherit (config.services) samba-wsdd;
cfg = config.services.samba; cfg = config.services.samba;
settingValue = value: settingValue = value:
@ -23,6 +27,31 @@ in {
settingPrimitive = oneOf [str int bool]; settingPrimitive = oneOf [str int bool];
settingType = oneOf [settingPrimitive (listOf settingPrimitive)]; settingType = oneOf [settingPrimitive (listOf settingPrimitive)];
in { 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 = { ldap = {
enable = mkEnableOption "LDAP"; enable = mkEnableOption "LDAP";
passdb = { passdb = {
@ -68,6 +97,34 @@ in {
default = null; 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 = { usershare = {
enable = mkEnableOption "usershare"; enable = mkEnableOption "usershare";
group = mkOption { group = mkOption {
@ -154,19 +211,39 @@ in {
config = { config = {
services.samba = { 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 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 = { 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" 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 [ idmap.domains = mkMerge [
(mkIf (cfg.ldap.enable && cfg.ldap.idmap.enable) { (mkIf (cfg.ldap.enable && cfg.ldap.idmap.enable) {
ldap = { ldap = {
backend = mkOptionDefault "ldap"; backend = mkOptionDefault "ldap";
domain = mkDefault cfg.ldap.idmap.domain; domain = mkAlmostOptionDefault cfg.ldap.idmap.domain;
settings = { settings = {
ldap_url = mkOptionDefault cfg.ldap.url; ldap_url = mkOptionDefault cfg.ldap.url;
}; };
@ -176,6 +253,10 @@ in {
settings = mkMerge ([ settings = mkMerge ([
{ {
"use sendfile" = mkOptionDefault true; "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) { (mkIf (cfg.passdb.smbpasswd.path != null) {
"passdb backend" = mkOptionDefault "smbpasswd:${cfg.passdb.smbpasswd.path}"; "passdb backend" = mkOptionDefault "smbpasswd:${cfg.passdb.smbpasswd.path}";
@ -194,8 +275,21 @@ in {
"dedicated keytab file" = mkIf (cfg.kerberos.keytabPath != null) (mkOptionDefault "dedicated keytab file" = mkIf (cfg.kerberos.keytabPath != null) (mkOptionDefault
"FILE:${cfg.kerberos.keytabPath}" "FILE:${cfg.kerberos.keytabPath}"
); );
"kerberos encryption types" = mkOptionDefault "strong";
"create krb5 conf" = mkOptionDefault false; "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 { (mkIf cfg.usershare.enable {
"usershare allow guests" = mkOptionDefault true; "usershare allow guests" = mkOptionDefault true;
"usershare max shares" = mkOptionDefault 16; "usershare max shares" = mkOptionDefault 16;
@ -221,6 +315,11 @@ in {
"-valid" = false; "-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 { systemd.services.samba-smbd = mkIf cfg.enable {
serviceConfig = let serviceConfig = let
@ -240,6 +339,17 @@ in {
"d ${cfg.usershare.path} 1770 root ${cfg.usershare.group}" "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 = { networking.firewall.interfaces.local = {
allowedTCPPorts = mkMerge [ allowedTCPPorts = mkMerge [
(mkIf (cfg.enable && !cfg.openFirewall) [139 445]) (mkIf (cfg.enable && !cfg.openFirewall) [139 445])

View file

@ -32,6 +32,7 @@ in {
serviceDnSuffix = mkDefault "cn=services,cn=accounts,"; serviceDnSuffix = mkDefault "cn=services,cn=accounts,";
hostDnSuffix = mkDefault "cn=computers,cn=accounts,"; hostDnSuffix = mkDefault "cn=computers,cn=accounts,";
hostGroupDnSuffix = mkDefault "cn=hostgroups,cn=accounts,"; hostGroupDnSuffix = mkDefault "cn=hostgroups,cn=accounts,";
idViewDnSuffix = mkDefault "cn=views,cn=accounts,";
sysAccountDnSuffix = mkDefault "cn=sysaccounts,cn=etc,"; sysAccountDnSuffix = mkDefault "cn=sysaccounts,cn=etc,";
domainDnSuffix = mkDefault "cn=ad,cn=etc,"; domainDnSuffix = mkDefault "cn=ad,cn=etc,";
}; };

View file

@ -1,27 +1,41 @@
{ {
inputs, inputs,
config, config,
access,
system,
lib, lib,
... ...
}: let }: let
inherit (inputs.self.lib.lib) mkBaseDn; inherit (inputs.self.lib.lib) mkBaseDn;
inherit (lib.modules) mkIf mkMerge mkDefault; inherit (lib.modules) mkIf mkMerge mkDefault;
inherit (lib.lists) any; inherit (lib.strings) toUpper removeSuffix;
inherit (lib.strings) toUpper hasInfix;
cfg = config.services.samba; cfg = config.services.samba;
inherit (config.networking) domain; inherit (config.networking) domain;
hasIpv4 = any (hasInfix ".") config.systemd.network.networks.eth0.address or []; inherit (config.users) ldap;
debugLogging = false;
ldapReadOnly = true;
in { in {
services.samba = { services.samba = {
enable = mkDefault true; enable = mkDefault true;
enableWinbindd = mkDefault false; enableWinbindd = mkDefault true;
enableNmbd = mkDefault hasIpv4; enableNmbd = mkDefault true;
securityType = mkDefault "user"; 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 = { ldap = {
enable = mkDefault true; enable = mkDefault true;
url = mkDefault "ldaps://ldap.int.${domain}"; url = mkDefault "ldaps://ldap.int.${domain}";
baseDn = mkDefault (mkBaseDn 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 ( adminPasswordPath = mkIf cfg.ldap.enable (
mkDefault config.sops.secrets.smb-ldap-password.path mkDefault config.sops.secrets.smb-ldap-password.path
); );
@ -30,8 +44,7 @@ in {
#backend = mkIf config.security.ipa.enable (mkDefault "ipasam"); #backend = mkIf config.security.ipa.enable (mkDefault "ipasam");
}; };
idmap = { idmap = {
enable = mkIf config.services.sssd.enable (mkDefault false); #enable = mkIf config.services.sssd.enable (mkDefault false);
domain = mkDefault cfg.settings.workgroup;
}; };
}; };
kerberos = mkIf (config.security.krb5.enable || config.security.ipa.enable) { kerberos = mkIf (config.security.krb5.enable || config.security.ipa.enable) {
@ -49,40 +62,78 @@ in {
mkDefault config.sops.secrets.smbpasswd.path mkDefault config.sops.secrets.smbpasswd.path
); );
settings = mkMerge [ { settings = mkMerge [ {
workgroup = "GENSOKYO"; "local master" = true;
"local master" = false; "preferred master" = true;
"preferred master" = false;
"winbind offline logon" = true; "winbind offline logon" = true;
"winbind scan trusted domains" = false; "winbind scan trusted domains" = false;
"winbind use default domain" = true; "winbind use default domain" = true;
"domain master" = false; "domain master" = true;
"server role" = "classic primary domain controller";
"domain logons" = true; "domain logons" = true;
"remote announce" = mkIf hasIpv4 [ "remote announce" = [
"10.1.1.255/${cfg.settings.workgroup}" "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 { } (mkIf cfg.ldap.enable {
"ldapsam:trusted" = true; "ldapsam:trusted" = true;
"ldapsam:editposix" = false; "ldapsam:editposix" = false;
"ldap user suffix" = "cn=users,cn=accounts"; "ldap user suffix" = removeSuffix "," ldap.userDnSuffix;
"ldap group suffix" = "cn=groups,cn=accounts"; "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 = { idmap.domains = {
nss = mkIf (!cfg.ldap.enable || !cfg.ldap.idmap.enable) { nss = mkIf (!cfg.ldap.enable || !cfg.ldap.idmap.enable) {
backend = "nss"; backend = "nss";
domain = "*"; domain = "*";
range.min = 8000; range.min = 8000;
#range.max = 8256; #range.max = 9000;
range.max = 65535;
}; };
ldap = mkIf (cfg.ldap.enable && cfg.ldap.idmap.enable) { ldap = mkIf (cfg.ldap.enable && cfg.ldap.idmap.enable) {
range.min = 8000; range.min = 8000;
#range.min = 8256; #range.max = 9000;
range.max = 65535;
readOnly = ldapReadOnly;
}; };
}; };
}; };
services.samba-wsdd = { services.samba-wsdd = {
enable = mkIf cfg.enable (mkDefault true); enable = mkIf cfg.enable (mkDefault true);
hostname = mkDefault config.networking.hostName; interface = mkDefault config.systemd.network.networks.eth0.name;
}; };
sops.secrets = { sops.secrets = {

View file

@ -98,6 +98,14 @@ in {
(mkIf config.services.tailscale.enable "mqtt.tail.${config.networking.domain}") (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 = { sso = {
inherit (nginx) group; inherit (nginx) group;
domain = virtualHosts.keycloak.serverName; domain = virtualHosts.keycloak.serverName;
@ -307,6 +315,9 @@ in {
}; };
}; };
}; };
services.samba.tls = {
useACMECert = "samba";
};
services.tailscale.advertiseExitNode = true; services.tailscale.advertiseExitNode = true;