diff --git a/devShells.nix b/devShells.nix index 1c188326..d86893ba 100644 --- a/devShells.nix +++ b/devShells.nix @@ -123,7 +123,7 @@ (mkWrapper rec { name = "ldapsearch"; attr = "pkgs.openldap"; - exe = "${name} -H ${ldapHostArg} -b ${ldapBaseDn} -o ldif_wrap=no"; + exe = ''${name} -H ${ldapHostArg} -b "''${LDAPSEARCH_BASE_DN-${ldapBaseDn}}" -o ldif_wrap=no''; }) (mkWrapper rec { name = "ldapadd"; diff --git a/modules/nixos/ldap/management.nix b/modules/nixos/ldap/management.nix new file mode 100644 index 00000000..6b920261 --- /dev/null +++ b/modules/nixos/ldap/management.nix @@ -0,0 +1,164 @@ +{ + config, + lib, + inputs, + pkgs, + ... +}: let + inherit (inputs.self.lib.lib) mapOptionDefaults; + inherit (lib.options) mkEnableOption; + inherit (lib.modules) mkIf mkOptionDefault; + inherit (lib.attrsets) attrValues; + inherit (lib.lists) filter; + inherit (lib.strings) concatStringsSep concatMapStringsSep escapeShellArgs; + inherit (config.users) ldap; + cfg = config.users.ldap.management; + enabledObjects = filter (object: object.enable) (attrValues cfg.objects); + smbSyncUsers = filter (user: user.samba.sync.enable) (attrValues cfg.users); + smbSyncGroups = filter (group: group.samba.sync.enable) (attrValues cfg.groups); + modifyObjects = filter (object: object.changeType == "modify") enabledObjects; + addObjects = filter (object: object.changeType == "add") enabledObjects; + deleteObjects = filter (object: object.changeType == "delete") enabledObjects; + additions = pkgs.writeText "ldap-management-add.ldap" ( + concatMapStringsSep "\n" (object: object.changeText) addObjects + ); + # TODO: split up adds and replaces so this can be done without `ldapmodify -c` + modifications = pkgs.writeText "ldap-management-modify.ldap" ( + concatMapStringsSep "\n" (object: object.changeText) modifyObjects + ); + deletions = pkgs.writeText "ldap-management-delete.ldap" ( + concatMapStringsSep "\n" (object: object.changeText) deleteObjects + ); + objectClassAttr = "objectClass"; + sidAttr = "ipaNTSecurityIdentifier"; + ntHashAttr = "ipaNTHash"; + authTypeAttr = "ipaUserAuthType"; + userSearchAttrs = [ objectClassAttr sidAttr authTypeAttr ntHashAttr ]; + groupSearchAttrs = [ objectClassAttr sidAttr ]; + managementScript = pkgs.writeShellScript "ldap-management.sh" '' + set -eu + + ldapsearch() { + command ldapsearch -QLLL -o ldif_wrap=no "$@" + } + + ldapmodify() { + command ldapmodify -Q "$@" + } + + ldap_parse() { + local LDAP_ATTR=$1 LDAP_LIMIT LDAP_LINE LDAP_COUNT=0 + shift 1 + local LDAP_LIMIT=''${1-1} + + while read -r LDAP_LINE; do + if [[ $LDAP_LIMIT -eq 0 ]]; then + break + fi + if [[ $LDAP_LINE = "$LDAP_ATTR:: "* ]]; then + printf '%s\n' "$LDAP_LINE" | cut -d ' ' -f 2- | base64 -d + elif [[ $LDAP_LINE = "$LDAP_ATTR: "* ]]; then + printf '%s\n' "$LDAP_LINE" | cut -d ' ' -f 2- + else + continue + fi + LDAP_COUNT=$((LDAP_COUNT+1)) + LDAP_LIMIT=$((LDAP_LIMIT-1)) + done + if [[ $LDAP_COUNT -eq 0 ]]; then + echo "$LDAP_ATTR not found" >&2 + return 1 + fi + } + + smbsync_group() { + local LDAP_GROUP_CN=$1 SMB_GROUP_DATA SMB_GROUP_SID + shift 1 + + echo "updating cn=''${LDAP_GROUP_CN},${ldap.groupDnSuffix} ..." >&2 + SMB_GROUP_DATA=$(ldapsearch -z1 \ + -b "${ldap.groupDnSuffix}${ldap.base}" \ + "(&(cn=$LDAP_GROUP_CN)(${objectClassAttr}=posixgroup))" \ + ${escapeShellArgs groupSearchAttrs} + ) + SMB_GROUP_SID=$(ldap_parse ${sidAttr} <<< "$SMB_GROUP_DATA") + ldapmodify <&2 + SMB_USER_DATA=$(ldapsearch -z1 \ + -b "${ldap.userDnSuffix}${ldap.base}" \ + "(&(uid=$LDAP_USER_UID)(${objectClassAttr}=posixaccount))" \ + ${escapeShellArgs userSearchAttrs} + ) + SMB_USER_SID=$(ldap_parse ${sidAttr} <<< "$SMB_USER_DATA") + SMB_USER_NTPASS=$(ldap_parse ${ntHashAttr} <<< "$SMB_USER_DATA" | xxd -p) + SMB_USER_NTPASS=''${SMB_USER_NTPASS^^} + ldapmodify <= 8000 && group.gid < 8256) config.users.groups; + management = { + users = mapAttrs (name: user: { + user.name = mkAlmostOptionDefault name; + samba = { + enable = mkDefault true; + sync.enable = mkDefault true; + accountFlags = { + noPasswordExpiry = mkDefault true; + }; + }; + }) ldapUsers; + groups = mapAttrs (name: group: { + group.name = mkAlmostOptionDefault name; + samba.enable = mkDefault true; + }) ldapGroups; + }; +in { + config.users.ldap = { + management = mkMerge [ management { + users = { + guest.user.enable = true; + admin = { + user.enable = true; + samba.enable = true; + }; + opl = { + user.enable = true; + samba = { + enable = true; + #sync.enable = true; + accountFlags = { + noPasswordExpiry = mkDefault true; + normalUser = true; + }; + }; + object.settings.settings = { + sambaNTPassword = "F7C2C5D78C24EACB73550B02BF5888E3"; + sambaLMPassword = "A5C96CDE7660B20BAAD3B435B51404EE"; + }; + }; + }; + groups = { + nogroup = { + group.enable = true; + samba.enable = true; + }; + guest = { + samba = { + enable = true; + groupType = 4; + sid = "S-1-5-32-546"; + }; + }; + admin = { + group.enable = true; + samba.enable = true; + }; + kyuuto-peeps = { + group.enable = true; + samba.enable = true; + }; + kyuuto = { + group.enable = true; + samba.enable = true; + }; + peeps = { + group.enable = true; + samba.enable = true; + }; + admins = { + samba = { + enable = true; + #sync.enable = true; + groupType = 4; + sid = "S-1-5-32-544"; + }; + }; + smb = { + name = "Default SMB Group"; + samba = { + #sync.enable = true; + groupType = 4; + sid = "S-1-5-32-545"; + }; + }; + }; + } ]; + }; +} diff --git a/systems/hakurei/nixos.nix b/systems/hakurei/nixos.nix index 8bdbe41d..a925d438 100644 --- a/systems/hakurei/nixos.nix +++ b/systems/hakurei/nixos.nix @@ -52,6 +52,8 @@ in { ./reisen-ssh.nix ]; + users.ldap.management.enable = true; + sops.secrets.cloudflared-tunnel-hakurei = { owner = config.services.cloudflared.user; }; diff --git a/tree.nix b/tree.nix index aba9e810..afed36be 100644 --- a/tree.nix +++ b/tree.nix @@ -55,9 +55,11 @@ ]; }; }; + "modules/nixos/ldap".functor.enable = true; "modules/nixos/network".functor.enable = true; "modules/nixos/nginx".functor.enable = true; "modules/nixos/steam".functor.enable = true; + "modules/nixos/users".functor.enable = true; "modules/meta".functor.enable = true; "modules/system".functor.enable = true; "modules/system/proxmox".functor.enable = true;