chore: ipa and sssd modules

This commit is contained in:
arcnmx 2024-04-05 15:01:05 -07:00
parent 0a48d9cf5d
commit 95e903697a
14 changed files with 983 additions and 65 deletions

12
lib.nix
View file

@ -38,14 +38,15 @@
mapListToAttrs = f: l: listToAttrs (map f l);
overrideOptionDefault = 1500;
overrideAlmostOptionDefault = 1400;
overrideDefault = 1000;
overrideNone = defaultOverridePriority; # 100
overrideAlmostForce = 75;
overrideForce = 50;
overrideVM = 10;
mkAlmostOptionDefault = mkOverride overrideAlmostOptionDefault;
mkAlmostForce = mkOverride overrideAlmostForce;
orderBefore = 500;
orderNone = 1000;
orderAfter = 1500;
@ -77,11 +78,16 @@ in {
eui64 mkWinPath mkBaseDn
toHexStringLower hexCharToInt
mapListToAttrs
mkAlmostOptionDefault mapOverride mapOptionDefaults mapAlmostOptionDefaults mapDefaults
overrideOptionDefault overrideAlmostOptionDefault overrideDefault overrideNone overrideForce overrideVM
mkAlmostOptionDefault mkAlmostForce mapOverride mapOptionDefaults mapAlmostOptionDefaults mapDefaults
overrideOptionDefault overrideAlmostOptionDefault overrideDefault overrideNone overrideAlmostForce overrideForce overrideVM
orderBefore orderNone orderAfter orderAlmostAfter
mkAlmostAfter;
inherit (inputs.arcexprs.lib) unmerged json;
};
gensokyo-zone = {
inherit inputs;
inherit (inputs) self;
inherit (inputs.self.lib) tree meta lib;
};
generate = import ./generate.nix {inherit inputs tree;};
}

View file

@ -4,10 +4,7 @@
...
}: let
hasConfigLib = options ? lib;
gensokyo-zone = {
inherit inputs;
inherit (inputs.self.lib) tree meta lib;
};
gensokyo-zone = inputs.self.lib.gensokyo-zone // {};
in {
config = {
${

5
modules/nixos/args.nix Normal file
View file

@ -0,0 +1,5 @@
{ gensokyo-zone, ... }: {
config.lib = {
inherit gensokyo-zone;
};
}

142
modules/nixos/ipa.nix Normal file
View file

@ -0,0 +1,142 @@
{
pkgs,
config,
lib,
gensokyo-zone,
modulesPath,
...
}: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault mkAlmostForce mapOptionDefaults;
inherit (lib.options) mkOption mkPackageOption;
inherit (lib.modules) mkIf mkMerge mkOptionDefault mkForce;
inherit (lib.attrsets) mapAttrsToList;
inherit (lib.strings) toLower;
cfg = config.security.ipa;
in {
options.security.ipa = with lib.types; {
package = mkPackageOption pkgs "freeipa" { };
overrideConfigs = {
krb5 = mkOption {
type = bool;
default = true;
description = "allow the ipa module to override krb5.conf";
};
sssd = mkOption {
type = bool;
default = true;
description = "allow the ipa module to override the sssd configuration";
};
ntp = mkOption {
type = bool;
default = false;
description = "allow the ipa module to override the ntp configuration";
};
};
};
config.services.sssd = let
inherit (config.services) sssd;
ipaDebugLevel = 65510;
in mkIf cfg.enable {
debugLevel = mkAlmostOptionDefault ipaDebugLevel;
domains = {
${cfg.domain} = {
ldap.extraAttrs.user = {
mail = "mail";
sn = "sn";
givenname = "givenname";
telephoneNumber = "telephoneNumber";
lock = "nsaccountlock";
};
settings = mapOptionDefaults {
id_provider = "ipa";
auth_provider = "ipa";
access_provider = "ipa";
chpass_provider = "ipa";
ipa_domain = cfg.domain;
ipa_server = [ "_srv_" cfg.server ];
ipa_hostname = "${config.networking.hostName}.${cfg.domain}";
cache_credentials = cfg.cacheCredentials;
krb5_store_password_if_offline = cfg.offlinePasswords;
dyndns_update = cfg.dyndns.enable;
dyndns_iface = cfg.dyndns.interface;
ldap_tls_cacert = "/etc/ipa/ca.crt";
} // {
krb5_realm = mkIf (toLower cfg.domain != toLower cfg.realm) (mkOptionDefault cfg.realm);
};
};
};
services = {
nss.settings = mapOptionDefaults {
homedir_substring = "/home";
};
pam.settings = mapOptionDefaults {
pam_pwd_expiration_warning = 3;
pam_verbosity = 3;
};
sudo = {
enable = mkAlmostOptionDefault true;
settings = mapOptionDefaults {
debug_level = ipaDebugLevel;
};
};
ssh.enable = mkAlmostOptionDefault true;
ifp = {
enable = mkAlmostOptionDefault true;
settings = mapOptionDefaults {
allowed_uids = cfg.ifpAllowedUids;
};
};
};
configText = mkIf (cfg.overrideConfigs.sssd) (mkAlmostOptionDefault null);
config = mkIf (sssd.configText != null) (mkAlmostForce sssd.configText);
};
config.security.krb5 = mkIf cfg.enable {
package = mkAlmostOptionDefault pkgs.krb5Full;
settings = {
libdefaults = mapOptionDefaults {
default_realm = cfg.realm;
dns_lookup_realm = false;
dns_lookup_kdc = true;
rdns = false;
ticket_lifetime = "24h";
forwardable = true;
udp_preference_limit = 0;
};
realms.${cfg.realm} = mapOptionDefaults {
kdc = "${cfg.server}:88";
master_kdc = "${cfg.server}:88";
admin_server = "${cfg.server}:749";
default_domain = cfg.domain;
pkinit_anchors = "/etc/ipa/ca.crt";
};
domain_realm = mkMerge [
(mapOptionDefaults {
".${cfg.domain}" = cfg.realm;
${cfg.domain} = cfg.realm;
})
(mapOptionDefaults {
${cfg.server} = cfg.realm;
})
];
dbmodules.${cfg.realm} = {
db_library = "${cfg.package}/lib/krb5/plugins/kdb/ipadb.so";
};
};
};
config.services.ntp = mkIf (cfg.enable && !cfg.overrideConfigs.ntp) {
servers = mkForce config.networking.timeServers;
};
config.environment.etc."krb5.conf" = let
inherit (config.security) krb5;
format = import (modulesPath + "/security/krb5/krb5-conf-format.nix") { inherit pkgs lib; } { };
in mkIf (cfg.enable && !cfg.overrideConfigs.krb5) {
text = mkForce (format.generate "krb5.conf" krb5.settings).text;
};
}

View file

@ -0,0 +1,136 @@
{ gensokyo-zone, pkgs, config, lib, ... }: let
inherit (gensokyo-zone.lib) mkBaseDn mapDefaults mkAlmostOptionDefault mapOptionDefaults domain;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkDefault mkOptionDefault mkForce;
inherit (lib.attrsets) mapAttrsToList;
inherit (lib.strings) toUpper concatStringsSep concatStrings;
inherit (config.security) krb5 ipa;
cfg = krb5.gensokyo-zone;
enabled = krb5.enable || ipa.enable;
subsection = attrs: "{\n" + concatStrings (mapAttrsToList (key: value: " ${key} = ${value}\n") attrs) + "}";
in {
options.security.krb5.gensokyo-zone = with lib.types; {
enable = mkEnableOption "realm";
host = mkOption {
type = str;
default = cfg.canonHost;
};
canonHost = mkOption {
type = str;
default = "idp.${cfg.domain}";
};
domain = mkOption {
type = str;
default = domain;
};
realm = mkOption {
type = str;
default = toUpper cfg.domain;
};
ca.cert = mkOption {
type = path;
};
ldap = {
baseDn = mkOption {
type = str;
default = mkBaseDn cfg.domain;
};
bind = {
dn = mkOption {
type = str;
default = "uid=peep,cn=sysaccounts,cn=etc,${cfg.ldap.base}";
};
passwordFile = mkOption {
type = nullOr str;
default = null;
};
};
urls = mkOption {
type = listOf str;
};
};
db.backend = mkOption {
type = enum [ "kldap" "ipa" ];
default = "kldap";
};
authToLocalNames = mkOption {
type = attrsOf str;
default = { };
};
};
config = {
security.krb5 = {
package = let
krb5-ldap = pkgs.krb5.override {
withLdap = true;
};
in mkIf (cfg.enable && cfg.db.backend == "kldap") (mkDefault pkgs.krb5-ldap or krb5-ldap);
settings = mkIf cfg.enable {
dbmodules = {
genso-kldap = mkIf (cfg.db.backend == "kldap") (mapDefaults {
db_library = "kldap";
ldap_servers = concatStringsSep " " cfg.ldap.urls;
ldap_kdc_dn = cfg.ldap.bind.dn;
ldap_kerberos_container_dn = cfg.ldap.baseDn;
} // {
ldap_service_password_file = mkIf (cfg.ldap.bind.passwordFile != null) (mkDefault cfg.ldap.bind.passwordFile);
});
genso-ipa = mkIf (cfg.db.backend == "ipa") (mapDefaults {
db_library = "${ipa.package}/lib/krb5/plugins/kdb/ipadb.so";
});
${cfg.realm} = mkIf ipa.enable (mkForce { });
};
realms.${cfg.realm} = mapDefaults {
kdc = "${cfg.host}:88";
master_kdc = "${cfg.host}:88";
admin_server = "${cfg.host}:749";
default_domain = cfg.domain;
pkinit_anchors = [ "FILE:${cfg.ca.cert}" ];
} // {
database_module = mkOptionDefault "genso-${cfg.db.backend}";
auth_to_local_names = mkIf (cfg.authToLocalNames != { }) (mkDefault (subsection cfg.authToLocalNames));
};
domain_realm = mapOptionDefaults {
${cfg.domain} = cfg.realm;
".${cfg.domain}" = cfg.realm;
};
libdefaults = mapOptionDefaults {
default_realm = cfg.realm;
dns_lookup_realm = false;
dns_lookup_kdc = true;
rdns = false;
ticket_lifetime = "24h";
forwardable = true;
udp_preference_limit = 0;
ignore_acceptor_hostname = true;
};
};
gensokyo-zone = {
ca.cert = let
caPem = pkgs.fetchurl {
name = "${cfg.canonHost}.ca.pem";
url = "https://freeipa.${cfg.domain}/ipa/config/ca.crt";
sha256 = "sha256-PKjnjn1jIq9x4BX8+WGkZfj4HQtmnHqmFSALqggo91o=";
};
in mkOptionDefault caPem;
db.backend = mkIf ipa.enable (mkAlmostOptionDefault "ipa");
ldap.urls = mkOptionDefault [
"ldaps://ldap.${cfg.domain}"
"ldaps://${cfg.canonHost}"
];
};
};
networking.timeServers = mkIf (cfg.enable && enabled) [ "2.fedora.pool.ntp.org" ];
security.ipa = mkIf cfg.enable {
certificate = mkDefault cfg.ca.cert;
basedn = mkDefault cfg.ldap.baseDn;
domain = mkDefault cfg.domain;
realm = mkDefault cfg.realm;
server = mkDefault cfg.canonHost;
ifpAllowedUids = [
"root"
] ++ config.users.groups.wheel.members;
dyndns.enable = mkDefault false;
};
};
}

View file

@ -73,7 +73,6 @@ in {
netgroup = mkMerge [
(mkBefore [ "files" ])
(mkAfter [ "nis" ])
(mkIf config.services.sssd.enable [ "sss" ])
];
};
environment.etc."nsswitch.conf".text = mkIf (nssDatabases.netgroup != [ ]) (mkAfter ''

View file

@ -0,0 +1,194 @@
{ gensokyo-zone, pkgs, config, lib, ... }: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault mapOptionDefaults mapAlmostOptionDefaults;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkAfter mkDefault mkOptionDefault;
inherit (config.security) krb5 ipa;
inherit (config.services) sssd;
genso = krb5.gensokyo-zone;
cfg = sssd.gensokyo-zone;
serverModule = { config, ... }: {
options = with lib.types; {
servers = mkOption {
type = nullOr (listOf str);
default = null;
};
backups = mkOption {
type = listOf str;
default = [ ];
};
serverName = mkOption {
type = str;
internal = true;
};
serverKind = mkOption {
type = enum [ "server" "uri" ];
default = "server";
internal = true;
};
settings = mkOption {
type = attrsOf (listOf str);
};
};
config = let
key = "${config.serverName}_${config.serverKind}";
keyBackups = "${config.serverName}_backup_${config.serverKind}";
in {
settings = {
${key} = mkIf (config.servers != null) (mkOptionDefault config.servers);
${keyBackups} = mkIf (config.backups != [ ]) (mkOptionDefault config.backups);
};
};
};
mkServerType = { modules }: lib.types.submoduleWith {
modules = [ serverModule ] ++ modules;
specialArgs = {
inherit gensokyo-zone pkgs;
nixosConfig = config;
};
};
mkServerOption = { name, kind ? "server" }: let
serverInfoModule = { ... }: {
config = {
serverName = mkOptionDefault name;
serverKind = mkAlmostOptionDefault kind;
};
};
in mkOption {
type = mkServerType {
modules = [ serverInfoModule ];
};
default = { };
};
in {
options.services.sssd.gensokyo-zone = with lib.types; {
enable = mkEnableOption "realm" // {
default = genso.enable;
};
ldap = {
bind = {
passwordFile = mkOption {
type = nullOr str;
default = null;
};
};
uris = mkServerOption { name = "ldap"; kind = "uri"; };
};
krb5 = {
servers = mkServerOption { name = "krb5"; };
};
ipa = {
servers = mkServerOption { name = "ipa"; } // {
default = {
inherit (cfg.krb5.servers) servers backups;
};
};
hostName = mkOption {
type = str;
default = config.networking.fqdn;
};
};
backend = mkOption {
type = enum [ "ldap" "ipa" ];
default = "ipa";
};
};
config = {
services.sssd = let
# or "ipaNTSecurityIdentifier" which isn't set for most groups, maybe check netgroups..?
objectsid = "sambaSID";
backendDomainSettings = {
ldap = mapAlmostOptionDefaults {
id_provider = mkDefault "ldap";
auth_provider = mkDefault "krb5";
access_provider = "ldap";
ldap_tls_cacert = "/etc/ssl/certs/ca-bundle.crt";
} // mapOptionDefaults {
ldap_access_order = [ "host" ];
ldap_schema = "IPA";
ldap_default_bind_dn = genso.ldap.bind.dn;
ldap_search_base = genso.ldap.baseDn;
ldap_user_search_base = "cn=users,cn=accounts,${genso.ldap.baseDn}";
ldap_group_search_base = "cn=groups,cn=accounts,${config.ldap.baseDn}";
ldap_user_uuid = "ipaUniqueID";
ldap_user_ssh_public_key = "ipaSshPubKey";
ldap_user_objectsid = objectsid;
ldap_group_uuid = "ipaUniqueID";
ldap_group_objectsid = objectsid;
};
ipa = mapOptionDefaults {
id_provider = "ipa";
auth_provider = "ipa";
access_provider = "ipa";
chpass_provider = "ipa";
dyndns_update = ipa.dyndns.enable;
dyndns_iface = ipa.dyndns.interface;
};
};
domainSettings = mapAlmostOptionDefaults {
ipa_hostname = cfg.ipa.hostName;
} // mapOptionDefaults {
enumerate = true;
ipa_domain = genso.domain;
krb5_realm = genso.realm;
cache_credentials = ipa.cacheCredentials;
krb5_store_password_if_offline = ipa.offlinePasswords;
#min_id = 8000;
#max_id = 8999;
};
in {
gensokyo-zone = {
krb5.servers.servers = mkMerge [
[ genso.host ]
(mkAfter [ "_srv" genso.canonHost ])
];
ldap.uris = {
servers = mkMerge [
(mkAfter [ "_srv" ])
genso.ldap.urls
];
};
};
domains = mkIf cfg.enable {
${genso.domain} = {
ldap = {
authtok = mkIf (cfg.backend == "ldap") {
passwordFile = mkIf (cfg.ldap.bind.passwordFile != null) (mkAlmostOptionDefault cfg.ldap.bind.passwordFile);
};
extraAttrs.user = {
mail = "mail";
sn = "sn";
givenname = "givenname";
telephoneNumber = "telephoneNumber";
lock = "nsaccountlock";
};
};
settings = mkMerge [
domainSettings
backendDomainSettings.${cfg.backend}
(mapAlmostOptionDefaults cfg.ldap.uris.settings)
(mapAlmostOptionDefaults cfg.krb5.servers.settings)
(mkIf (cfg.backend == "ipa") (mapAlmostOptionDefaults cfg.ipa.servers.settings))
];
};
};
services = mkIf cfg.enable {
nss.settings = mapOptionDefaults {
homedir_substring = "/home";
};
pam.settings = mapOptionDefaults {
pam_pwd_expiration_warning = 3;
pam_verbosity = 3;
};
sudo.enable = mkIf (!sssd.services.pam.enable) (mkDefault false);
ssh.enable = mkIf (!sssd.services.pam.enable) (mkDefault false);
ifp = {
enable = mkAlmostOptionDefault true;
settings = mapOptionDefaults {
allowed_uids = ipa.ifpAllowedUids;
};
};
};
};
};
}

210
modules/nixos/sssd/sssd.nix Normal file
View file

@ -0,0 +1,210 @@
{
config,
options,
lib,
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault mapOptionDefaults;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkOptionDefault mkForce;
inherit (lib.attrsets) mapAttrs filterAttrs attrNames attrValues listToAttrs mapAttrsToList nameValuePair;
inherit (lib.lists) filter isList concatMap;
inherit (lib.strings) toUpper concatMapStringsSep replaceStrings;
inherit (lib.trivial) flip;
inherit (lib) generators;
cfg = config.services.sssd;
mkValuePrimitive = value:
if value == true then "True"
else if value == false then "False"
else toString value;
toINI = generators.toINI {
mkKeyValue = generators.mkKeyValueDefault {
mkValueString = value:
if isList value then concatMapStringsSep ", " mkValuePrimitive value
else mkValuePrimitive value;
} " = ";
};
primitiveType = with lib.types; oneOf [ str int bool ];
valueType = with lib.types; oneOf [ primitiveType (listOf primitiveType) ];
settingsType = lib.types.attrsOf valueType;
serviceModule = { name, ... }: {
options = with lib.types; {
enable = mkEnableOption "${name} service";
name = mkOption {
type = str;
default = name;
readOnly = true;
};
settings = mkOption {
type = settingsType;
default = { };
};
};
};
nssModule = { nixosConfig, ... }: {
options = {
# TODO: passwd.enable = mkEnableOption "passwd" // { default = true; };
shadow.enable = mkEnableOption "shadow" // { default = nixosConfig.services.sssd.services.pam.enable; };
netgroup.enable = mkEnableOption "netgroup" // { default = true; };
};
};
domainModule = { name, ... }: {
options = with lib.types; {
enable = mkEnableOption "domain" // {
default = true;
};
domain = mkOption {
type = str;
default = name;
};
settings = mkOption {
type = settingsType;
};
};
};
domainLdapModule = { config, ... }: let
cfg = config.ldap;
in {
options.ldap = with lib.types; {
extraAttrs.user = mkOption {
type = attrsOf str;
default = { };
};
authtok = {
type = mkOption {
type = enum [ "password" "obfuscated_password" ];
default = "password";
};
password = mkOption {
type = nullOr str;
default = null;
};
passwordFile = mkOption {
type = nullOr path;
default = null;
};
passwordVar = mkOption {
type = str;
internal = true;
default = "SSSD_AUTHTOK_" + replaceStrings [ "-" "." ] [ "_" "_" ] (toUpper config.domain);
};
};
};
config = let
authtokConfig = mkIf (cfg.authtok.password != null || cfg.authtok.passwordFile != null) {
ldap_default_authtok_type = mkOptionDefault cfg.authtok.type;
ldap_default_authtok = mkOptionDefault (
if cfg.authtok.passwordFile != null then "\$${cfg.authtok.passwordVar}"
else cfg.authtok.password
);
};
extraAttrsConfig = mkIf (cfg.extraAttrs.user != { }) {
ldap_user_extra_attrs = let
mkAttr = name: attr: "${name}:${attr}";
in mapAttrsToList mkAttr cfg.extraAttrs.user;
};
in {
settings = mkMerge [
authtokConfig
extraAttrsConfig
];
};
};
in {
options.services.sssd = with lib.types; {
debugLevel = mkOption {
type = ints.between 16 65520;
default = 16;
};
domains = mkOption {
type = attrsOf (submoduleWith {
modules = [ domainModule domainLdapModule ];
specialArgs = {
nixosConfig = config;
};
});
default = {
shadowutils.settings = mapOptionDefaults {
id_provider = "proxy";
proxy_lib_name = "files";
auth_provider = "proxy";
proxy_pam_target = "sssd-shadowutils";
proxy_fast_alias = true;
};
};
};
services = let
mkServiceOption = name: { modules ? [ ] }: mkOption {
type = submoduleWith {
modules = [ serviceModule ] ++ modules;
specialArgs = {
inherit name;
nixosConfig = config;
};
};
};
services = {
nss = { modules = [ nssModule ]; };
pam = { };
ifp = { };
sudo = { };
autofs = { };
ssh = { };
pac = { };
};
in mapAttrs mkServiceOption services;
settings = mkOption {
type = attrsOf settingsType;
};
configText = mkOption {
type = nullOr lines;
};
};
config.services.sssd = let
enabledDomains = filter (domain: domain.enable) (attrValues cfg.domains);
enabledServices = filterAttrs (_: service: service.enable) cfg.services;
in {
settings = let
serviceSettings = mapAttrs (name: service: mapOptionDefaults service.settings) enabledServices;
defaultSettings = {
sssd = mapOptionDefaults {
config_file_version = 2;
debug_level = cfg.debugLevel;
services = mapAttrsToList (_: service: service.name) enabledServices;
domains = map (domain: domain.domain) enabledDomains;
};
};
domainSettings = map (domain: {
"domain/${domain.domain}" = mapAttrs (_: mkOptionDefault) domain.settings;
}) enabledDomains;
settings = [ defaultSettings serviceSettings ] ++ domainSettings;
in mkMerge settings;
services = {
nss.enable = mkAlmostOptionDefault true;
pam.enable = mkAlmostOptionDefault true;
ifp.settings = let
extraUserAttrs = listToAttrs (concatMap (domain: map (flip nameValuePair {}) (attrNames domain.ldap.extraAttrs.user)) enabledDomains);
mkExtraAttr = name: _: "+${name}";
in {
user_attributes = mkIf (extraUserAttrs != { }) (mkOptionDefault (
mapAttrsToList mkExtraAttr extraUserAttrs
));
};
sudo = { };
autofs = { };
ssh = { };
pac = { };
};
configText = mkOptionDefault (toINI cfg.settings);
config = mkIf (cfg.configText != null) (mkAlmostOptionDefault cfg.configText);
};
config.system.nssDatabases = let
inherit (cfg.services) nss;
in mkIf cfg.enable {
${if options ? system.nssDatabases.netgroup then "netgroup" else null} = mkIf (nss.enable && nss.netgroup.enable) [ "sss" ];
shadow = mkIf (!nss.enable || !nss.shadow.enable) (
mkForce [ "files" ]
);
};
}

View file

@ -100,6 +100,7 @@ in {
}) config.builder);
specialArgs = {
inherit name inputs std meta;
inherit (inputs.self.lib) gensokyo-zone;
systemType = config.folder;
system = config;
};

View file

@ -1,15 +1,5 @@
{ inputs, pkgs, config, lib, ... }: let
inherit (inputs.self.lib.lib) mkBaseDn;
inherit (lib.modules) mkIf mkDefault mkOptionDefault;
inherit (lib.strings) toUpper;
inherit (config.networking) domain;
cfg = config.security.ipa;
baseDn = mkBaseDn domain;
caPem = pkgs.fetchurl {
name = "idp.${domain}.ca.pem";
url = "https://freeipa.${domain}/ipa/config/ca.crt";
sha256 = "sha256-PKjnjn1jIq9x4BX8+WGkZfj4HQtmnHqmFSALqggo91o=";
};
{ config, lib, ... }: let
inherit (lib.modules) mkDefault;
in {
# NOTE: requires manual post-install setup...
# :; kinit admin
@ -18,54 +8,18 @@ in {
# :; ipa-getkeytab -k /tmp/krb5.keytab -s idp.${domain} -p ${serviceName}/idp.${domain}@${toUpper domain}
# once the sops secret has been updated with keytab...
# :; systemctl restart sssd
imports = [
./krb5.nix
./sssd.nix
];
config = {
users.ldap = {
base = mkDefault baseDn;
server = mkDefault "ldaps://ldap.local.${domain}";
samba.domainSID = mkDefault "S-1-5-21-1535650373-1457993706-2355445124";
#samba.domainSID = mkDefault "S-1-5-21-208293719-3143191303-229982100"; # HAKUREI
userDnSuffix = mkDefault "cn=users,cn=accounts,";
groupDnSuffix = mkDefault "cn=groups,cn=accounts,";
permissionDnSuffix = mkDefault "cn=permissions,cn=pbac,";
privilegeDnSuffix = mkDefault "cn=privileges,cn=pbac,";
roleDnSuffix = mkDefault "cn=roles,cn=accounts,";
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,";
};
security.ipa = {
enable = mkDefault true;
certificate = mkDefault caPem;
basedn = mkDefault baseDn;
chromiumSupport = mkDefault false;
domain = mkDefault domain;
realm = mkDefault (toUpper domain);
server = mkDefault "idp.${domain}";
ifpAllowedUids = [
"root"
] ++ config.users.groups.wheel.members;
dyndns.enable = mkDefault false;
};
sops.secrets = {
krb5-keytab = mkIf cfg.enable {
mode = "0400";
path = "/etc/krb5.keytab";
};
};
systemd.services.krb5-host = let
krb5-host = pkgs.writeShellScript "krb5-host" ''
set -eu
kinit -k host/${config.networking.fqdn}
'';
in mkIf cfg.enable {
path = [ config.security.krb5.package ];
serviceConfig = {
Type = mkOptionDefault "oneshot";
ExecStart = [ "${krb5-host}" ];
overrideConfigs = {
krb5 = mkDefault false;
sssd = mkDefault false;
};
};
};

90
nixos/krb5.nix Normal file
View file

@ -0,0 +1,90 @@
{ inputs, pkgs, config, access, lib, ... }: let
inherit (inputs.self.lib.lib) mkAlmostOptionDefault mapAlmostOptionDefaults;
inherit (lib.modules) mkIf mkMerge mkBefore mkDefault mkOptionDefault;
inherit (lib.strings) replaceStrings;
inherit (config.security) ipa;
cfg = config.security.krb5;
enabled = cfg.enable || ipa.enable;
domain = cfg.gensokyo-zone.domain;
in {
config = {
security.krb5 = {
enable = mkIf (!ipa.enable) (mkDefault true);
settings = {
libdefaults = mapAlmostOptionDefaults {
dns_lookup_kdc = false;
rdns = false;
};
};
gensokyo-zone = let
toLdap = replaceStrings [ "idp." ] [ "ldap." ];
lanName = access.getHostnameFor "freeipa" "lan";
localName = access.getHostnameFor "freeipa" "local";
ldapLan = toLdap lanName;
ldapLocal = toLdap localName;
in {
enable = mkDefault true;
host = mkAlmostOptionDefault lanName;
ldap = {
urls = mkMerge [
(mkOptionDefault (mkBefore [ "ldaps://${ldapLan}" ]))
(mkIf (ldapLan != ldapLocal) (mkOptionDefault (mkBefore [ "ldaps://${ldapLan}" ])))
];
bind.passwordFile = mkIf (cfg.gensokyo-zone.db.backend == "kldap") config.sops.secrets.gensokyo-zone-krb5-passwords.path;
};
};
};
users.ldap = {
base = mkDefault cfg.gensokyo-zone.ldap.baseDn;
server = mkDefault "ldaps://ldap.local.${domain}";
samba.domainSID = mkDefault "S-1-5-21-1535650373-1457993706-2355445124";
#samba.domainSID = mkDefault "S-1-5-21-208293719-3143191303-229982100"; # HAKUREI
userDnSuffix = mkDefault "cn=users,cn=accounts,";
groupDnSuffix = mkDefault "cn=groups,cn=accounts,";
permissionDnSuffix = mkDefault "cn=permissions,cn=pbac,";
privilegeDnSuffix = mkDefault "cn=privileges,cn=pbac,";
roleDnSuffix = mkDefault "cn=roles,cn=accounts,";
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,";
};
networking.timeServers = [ "2.fedora.pool.ntp.org" ];
security.ipa = {
chromiumSupport = mkDefault false;
};
services.sssd = {
domains.${domain}.settings = {
enumerate = true;
};
};
systemd.services.krb5-host = let
krb5-host = pkgs.writeShellScript "krb5-host" ''
set -eu
kinit -k host/${config.networking.fqdn}
'';
in mkIf enabled {
path = [ config.security.krb5.package ];
serviceConfig = {
Type = mkOptionDefault "oneshot";
ExecStart = [ "${krb5-host}" ];
};
};
sops.secrets = let
sopsFile = mkDefault ./secrets/krb5.yaml;
in mkIf enabled {
krb5-keytab = {
mode = "0400";
path = "/etc/krb5.keytab";
};
gensokyo-zone-krb5-passwords = mkIf (cfg.gensokyo-zone.db.backend == "kldap") {
inherit sopsFile;
};
};
};
}

122
nixos/secrets/krb5.yaml Normal file
View file

@ -0,0 +1,122 @@
gensokyo-zone-krb5-passwords: ENC[AES256_GCM,data:5NiixDRjvTVUfqdvQgd9ctLZhwSrBy7LMi2py9vNrZ+OhMW6HGKtb6HsAca9/dO8yDyVhdG7ccn6Nj4Zqzw9E3Tf0ch7Ckrd3ffphSE+ETF/HscEj4zn7KijytfdDGFdL7o2yA5R0Ko3qgZO+9q8zM8ZuqsIcsIKSqn+,iv:/Ojpl7nY0NnwLmkE8qecTV2KGLfmHfwXM1uUw2XKY7g=,tag:KuAuY8nS6LB1FS6G++vtLw==,type:str]
gensokyo-zone-sssd-passwords: ENC[AES256_GCM,data:Eh0rJxPC3WgBitpvxaoLxNBYsSXZm0n3rgDvXXeygXZMmMiyXkpFmnK+TL4DDZdug7eoTCe85oUjpqRA,iv:Xl60ZXeSUJPN3V1DnyXkktHmECl1v1yOr9DOzYKW3fA=,tag:AQZcT2/3aoDNXq2mzo62AA==,type:str]
gensokyo-zone-krb5-peep-password: ENC[AES256_GCM,data:H5l++ng5L23gCHZUA7CrFqnPS/aPXh86lSoyRwTSXNY=,iv:9KaDTfS7xWZfnh6dCjxD8KcxIiDVNHWVFySr44OG8iY=,tag:JpSS6V5YBxqsG40pr0mLNA==,type:str]
sops:
shamir_threshold: 1
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age12ze362pu5mza6ef9akrptr7hfe4auaqul4rkta7kyy2tnrstqensgmujeq
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0QXUyUlQxai9HYlRFRXZQ
VTRpU0RJVEpVTjFLKzdwMWZ3YmtxVGRkUGxFCmVHM2phRks1aXJtVHRZWFo4TGNs
aUMwc2FRVExjNmhkUFQwaklaQ0NNREUKLS0tIDZleW5DMlpOSUE2RzNkSHNoY1ZH
T3VpeVhSRXNaNDhzL3JuUDJiNnBrclkKJ8+VlybUeq6Xyh7SU8ib6KfitxnY/Fsi
bp66PEuxIEKLrNMFkGQdFXld4AnAB4dDy8x1d8Hzd4J2MrzgEXl3bg==
-----END AGE ENCRYPTED FILE-----
- recipient: age176uyyyk7veqnzmm8xzwfhf0u23m6hm02cldlfkldunqe6std0gcq6lg057
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxcjc0c0F4dGdUREdkYVND
QmNwVmtXTERIRWtFSXdQN2k0K25kYTlSczF3ClVnMHBlMnlMSU84MnJnL2o1NnZ5
bDZEMkpMa0kyVVRONVNnMFMwdXErVTQKLS0tIDVpUHV1TFZZWW5nckc5UklDOHd1
ZS95ZUYzSWFQczJ6YlVTMXhBVmdZT3MKDM/HOuRbnxWQVpeUxKsiu9P4NnAzcaXJ
66IB5/Bmqr+3lcm65EWMzDyzp6KDgg5NkGWMhbHp1Lc1RJ6Em1Ld1w==
-----END AGE ENCRYPTED FILE-----
- recipient: age15hmlkd9p5rladsjzpmvrh6u34xvggu9mzdsdxdj3ms43tltxeuhq4g7g9k
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBIS2RBdmd1YVQyS0g2ZTE1
ZEN3WU1xd2ZPNFBnTHgxbTNjMFVmcXA5NVZNCmxZVzRjbTJ5K01paE9JYk54MGp2
THRYcVVkbVFvUWQ2NWcwUVEvY2JjVkUKLS0tIGlQZ1F0cWZFU0JGZHJCSUo0d29S
cGZNL0xQdVFhem1RODg3dGh4bW5aSDgKomItS8jfMy6qJFYjHDS7ozf5D8YYzB68
RvR4aQsKBOEHLdGgpXRq840eli5a9HoUxg2DzAAFSy8W3cbHjt9ADw==
-----END AGE ENCRYPTED FILE-----
- recipient: age10t6kc5069cyky929vvxk8aznqyxpkx3k5h5rmlyz83xtjmr22ahqe8mzes
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA3SHlnemxlR1BYeXM5VjBY
aHF5MCt1dGYyd2N1Y1pHaHFnWFV4UFBNa2pJClRGK0tYSmJlZDBxY3lUWG8xNjhY
bTVyNndnRzMwTmxabm1ZV2grZVJyTGcKLS0tIDFUMjRNMnNaeGNYM1E2cUZ2UUFa
UnZRNlRGNmpxelErc1BCMGY1OEhPaXcKwpxQRZQcf0soOemG/NgGJ6g+8MrmVuSJ
LE2MoCJ3v43uX8aS5iCL6g3FDEQwjBv0VHYvnQLvM5FxMOk2Eo02aw==
-----END AGE ENCRYPTED FILE-----
- recipient: age1a2quf2ekkj94ygu7wgvhrvh44fwn32c0l2cwvgvjh23wst90s54szdsvgr
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPUngzU1RGREQxaW5jYzdk
NEFUakVhMy8vZ2IwM3Z4Wi9rWm02cGNqalZzCjRtbitrMHVqZFNqN216TlAwa0xU
dHQ5SDNxbjhzUmdyTG1QbzI0dGN1Zk0KLS0tIGRRMTdnUEprSzI2aVlwOExGOERF
dHpHV1dWRk1WTHp0UUI1RkVvYVB5TlkKrPr60DHnznQzXzuM4zDmkp2SSNBfTW7b
vmctyAYGQIrLm6xQrcNIEkT6qazY3uofu9/hd1YWgaNTu6GaYWy+Lg==
-----END AGE ENCRYPTED FILE-----
- recipient: age16klpkaut5759dut8mdm3jn0rnp8w6kxyvs9n6ntqrdsayjtd7upqlvw489
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmbjBZTzBwcm1lZHpuMnoy
SG1HRFdDRFczbS9LaUttNC81WmRkUHV2THdJCklXN2VJZFVnRnhBSDNJOFgzNGRp
YXlRUTRzS1JiZW9zMVVxWTQreG14TFUKLS0tIFN0cG42Mnl3eGJES0JPT2FlY2xX
Ny9MSncxMGpGSVhYVjIzNGlSaU1jc2MKirF46sSIENQjmgf/OGxrl0C2wJmwul8w
wKjOr+OUIGT9x4pr52O/KwF17KGvoG2Ksa/Qscui49LwFId8YQIl4g==
-----END AGE ENCRYPTED FILE-----
- recipient: age13qgddr326g5je0fpq2r3k940vsr3fh9nlvl9xtcxk3xg2x0k3vsq7pvzaj
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMWXcxZlJuUzVFWkZpbjlK
cFFzYUh0Q2lBTEhVNjhYTWlsOGpUQlZ1bVVRCkI5OVNzRTllb0tyNHRXQ0x0S0xa
NWpad21Vc2xTRDNyVGNhbDFnWHZhb0UKLS0tIGhGbFM5cGcwT1ZaVWwzRjcySW9P
NEVvVXlPNlJQUEhoQTg1OUlmL3RPRWsKQSHafxdVc2oip6rMhlgyj0qRNDng8umL
CJBfa877OwmaLSXb17k+zX6HgikNT6Bi+ktGlLx+KUm3a8xeupF6vw==
-----END AGE ENCRYPTED FILE-----
- recipient: age1ktmx2szedfnpe5xumnzs8vkk0ffqgga6ved3drtksg9pye6ndsnsnqq488
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjVUtVOEV3ei9lRjRud1lK
R3NiblduVVZPaVJyRzlzM2RDR3UxYVZ2bkNBCjFtUVBaNWRKSExsOFh5eUU0TEpW
QWM1U2hrdU15TEZHb21SUkZpcEdWdlEKLS0tIG40c0NhcGJyZSt5dFltSlBTU2R2
TFM4RFNsYmVvWkt4WG5KVjd5aFZmdzAKr5vX/73ZTUa5W6laIIWnEA8HGC+OIjXM
E3mGAMhZbCh1O1NyuTsMErtLDzCL+C60Wgbk+wfcvMy5/pauR57eiQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2024-04-07T21:09:47Z"
mac: ENC[AES256_GCM,data:MjaCgpvMujcEJ3DrJ7vWEtZ3h2nURR1oZPrgxbXqeXD7gQleWn0BCdecP+A4IEE7tAS54H6btYcru/34wYu6R4UJBgh5W9C40ihhaSvXDASAvgXNkH/6jAFJxTuQYR+bT7fU7N1rBq8zzqHoytwV1AvNS9rmP3WJZ8FXE6Cf8O4=,iv:Dh9fCRWIq8pO7tdsrmUG2na47+BTZ5hiiJ3s7Bg+rCU=,tag:6BCECIlWyq/jc/KP/bC7ZA==,type:str]
pgp:
- created_at: "2024-04-07T21:09:42Z"
enc: |-
-----BEGIN PGP MESSAGE-----
hQIMA82M54yws73UAQ/9G0n0PQlsKsXbVbfV/BKbrHzKvzE5q8gLc9BngyPQo3UD
bjNA8u1FaPzdcA30InuYsIS+X/liFAX84Kv/b7UrkzQJnwDY1ijgxTUzDTeL5IEO
r13M/xzXNVtwsLFz0zMiRiXLtD5xaiUYaad0iH4wbZVMBJQRw4PSuq0r9whAyAC9
buj/ffKNboCOnSNlAbCWbBMKK8p25ao+Bubqikgyi/dwjQBEgr1/Q/LbrHdf73eF
ifyEq5/1o6SYiMpKnmRkJgpWvbQFNs2APXTpT65shs5a3smQy/h9hq4AKttSf6K1
fT5vGfAvp0j73H87mAw78B/AQPI5cVQk2jYeZlN5eANf+FaGmHALDQFfUY1OiSK2
PoSGgz3ZeRfG4U9v3Rc+9pTsSU+SOQne6oTvujAxYvoKQuSOHh/7JpAHviE3JLiP
sJBFn0g0N6sOzYoKEIONJJS2raRcUbEZYdR2rAmkSjllUGBXNBsxLC1OTf9+M3SF
PN+AiL8qPOA24Fn7nMzHL8xpbeM7Har7EXaH24KNiiG/Kp5J0zaGt5ik30y0a9Cn
7BFoFt6rdFAalFePA0WASwPfKdy87/BU+sRuElrSfYLnkT51FWwC/sKsMrjsIbtw
e8toZM3YjtfcOYx6UHJs4jQoBBGdFgTZH4yHkgCbuGzEKWvM/yHvE/ZCCRaDtfXS
XgHXZAkN/ztsDvl1pORDLbB5IgLJ1mmDGFqVvYrWsW3NvzYQyEDdqtqVf/wz3U4z
KgJ9WVFJjcKrJ1ZkAnPnnsHjlUuTj2OCyjWlL3/4ZXzoGE8QmaJ/iw9TFIMiS/I=
=f/eO
-----END PGP MESSAGE-----
fp: CD8CE78CB0B3BDD4
- created_at: "2024-04-07T21:09:42Z"
enc: |-
-----BEGIN PGP MESSAGE-----
hQEMA2W9MER3HLb7AQf/QtkcZJzP7gxoPr0PzJ4rV/PYuKkSCAhJuiAuTpE+lNvx
4mSsI2x6Hz59RYOYF7EylQ0HTEzjC/baMrgSS9zbbrKwfYdMvAtT6GBsBns4x3y9
XAPngVr2SQjm4In7TErbW/Lu81fl+GOU31Z1i7sE3bEpKEOKFSJTlzHJZoLgQcGl
AZb4b+ra9yRYJShM/qqIbR0RBhUX8aCeRZVJHy6IJFlU+3jur6kQtWUEyRVcp7Oj
SAwUpfTnZtqVa1Mol8h76NIHNRl8XGr7OeLCSMhItHVn9YiEKEVCfr+Gq9FOOEov
EkWsKp0RxsOXt6mkG2/HuNi5UghkqhFY2NgqDE8dv9JeAX/2fvadHuoDZrNx9Ygg
+ba9P0JoQnR/NxwTSylrwecNmo8TtSCd82bRb27GzKj0spxjD2CLhkuA1ETIJe/w
TAtqSpljTvASxUzsXQ8TOSY/M8r9iGUlQo7V0e8oFw==
=8CQe
-----END PGP MESSAGE-----
fp: 65BD3044771CB6FB
unencrypted_suffix: _unencrypted
version: 3.8.1

60
nixos/sssd.nix Normal file
View file

@ -0,0 +1,60 @@
{ gensokyo-zone, access, config, lib, ... }: let
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.modules) mkIf mkBefore mkAfter mkDefault;
inherit (lib.strings) replaceStrings;
cfg = config.services.sssd;
in {
imports = [
./krb5.nix
];
config = {
services.sssd = {
enable = (mkDefault true);
gensokyo-zone = let
toService = service: replaceStrings [ "idp." ] [ "${service}." ];
toFreeipa = toService "freeipa";
toLdap = toService "ldap";
lanName = access.getHostnameFor "freeipa" "lan";
localName = access.getHostnameFor "freeipa" "local";
tailName = access.getHostnameFor "hakurei" "tail";
localToo = lanName != localName;
servers = mkBefore [
lanName
(mkIf localToo localName)
];
backups = mkAlmostOptionDefault (mkAfter [
(toFreeipa lanName)
(mkIf config.services.tailscale.enable (toFreeipa tailName))
]);
in {
krb5.servers = {
inherit servers backups;
};
ldap = {
uris = {
backups = mkAlmostOptionDefault (mkAfter [
(mkIf config.services.tailscale.enable (toLdap tailName))
]);
};
bind.passwordFile = mkIf (cfg.gensokyo-zone.backend == "ldap") config.sops.secrets.gensokyo-zone-peep-passwords.path;
};
};
environmentFile = mkIf (cfg.gensokyo-zone.enable && cfg.gensokyo-zone.backend == "ldap") (mkAlmostOptionDefault
config.sops.secrets.gensokyo-zone-sssd-passwords.path
);
};
sops.secrets = let
sopsFile = mkDefault ./secrets/krb5.yaml;
in mkIf (cfg.enable && cfg.gensokyo-zone.enable) {
gensokyo-zone-krb5-peep-password = mkIf (cfg.gensokyo-zone.enable && cfg.gensokyo-zone.backend == "ldap") {
inherit sopsFile;
};
# TODO: this shouldn't be needed, module is incomplete :(
gensokyo-zone-sssd-passwords = mkIf (cfg.gensokyo-zone.enable && cfg.gensokyo-zone.backend == "ldap") {
inherit sopsFile;
};
};
};
}

View file

@ -56,6 +56,8 @@
};
};
"modules/nixos/ldap".functor.enable = true;
"modules/nixos/krb5".functor.enable = true;
"modules/nixos/sssd".functor.enable = true;
"modules/nixos/network".functor.enable = true;
"modules/nixos/nginx".functor.enable = true;
"modules/nixos/steam".functor.enable = true;