aboutsummaryrefslogblamecommitdiff
path: root/systems/eldiron/databases/openldap/default.nix
blob: 7cd15da41a5d97faa084c5408f36f056bf5f06c2 (plain) (tree)















































































































































































































































































































                                                                                                                                
{ lib, pkgs, config, openldap, ... }:
let
  cfg = config.myServices.databases.openldap;
in
{
  options.myServices.databases = {
    openldap = {
      enable = lib.mkOption {
        default = false;
        example = true;
        description = "Whether to enable ldap";
        type = lib.types.bool;
      };
      baseDn = lib.mkOption {
        type = lib.types.str;
        description = ''
          Base DN for LDAP
        '';
      };
      rootDn = lib.mkOption {
        type = lib.types.str;
        description = ''
          Root DN
        '';
      };
      rootPw = lib.mkOption {
        type = lib.types.str;
        description = ''
          Root (Hashed) password
        '';
      };
      accessFile = lib.mkOption {
        type = lib.types.path;
        description = ''
          The file path that defines the access
        '';
      };
      dataDir = lib.mkOption {
        type = lib.types.path;
        default = "/var/lib/openldap/mdb";
        description = ''
          The directory where Openldap stores its data.
        '';
      };
      socketsDir = lib.mkOption {
        type = lib.types.path;
        default = "/run/openldap";
        description = ''
          The directory where Openldap puts sockets and pid files.
          '';
      };
      # Output variables
      pids = lib.mkOption {
        type = lib.types.attrsOf lib.types.path;
        default = {
          pid  = "${cfg.socketsDir}/slapd.pid";
          args = "${cfg.socketsDir}/slapd.args";
        };
        readOnly = true;
        description = ''
          Slapd pid files
          '';
      };
    };
  };

  config = lib.mkIf cfg.enable {
    myServices.dns.zones."immae.eu".subdomains.ldap =
      with config.myServices.dns.helpers; ips servers.eldiron.ips.main;

    nixpkgs.overlays = [
      (self: super: {
        openldap_libressl_cyrus = (self.openldap.override {
          openssl = self.libressl;
          cyrus_sasl = self.cyrus_sasl.overrideAttrs (old: {
            configureFlags = old.configureFlags ++ [ "--with-configdir=/etc/sasl2" ];
          });
        }).overrideAttrs (old: {
          configureFlags = old.configureFlags ++ [ "--with-cyrus-sasl" "--enable-spasswd" ];
        });
      })
    ];

    secrets.keys = {
       "ldap/password" = {
        permissions = "0400";
        user = "openldap";
        group = "openldap";
        text = "${cfg.rootPw}";
      };
      "ldap/access" = {
        permissions = "0400";
        user = "openldap";
        group = "openldap";
        text = builtins.readFile cfg.accessFile;
      };
      "ldap" = {
        permissions = "0500";
        user = "openldap";
        group = "openldap";
        isDir = true;
      };
    };
    users.users.openldap.extraGroups = [ "keys" ];
    networking.firewall.allowedTCPPorts = [ 636 389 ];

    security.acme.certs."ldap" = {
      group = "openldap";
      domain = "ldap.immae.eu";
      postRun = ''
        systemctl restart openldap.service
      '';
    };

    services.filesWatcher.openldap = {
      restart = true;
      paths = [ config.secrets.fullPaths."ldap" ];
    };

    services.openldap = {
      enable = true;
      urlList = [ "ldap://" "ldaps://" ];
      package = pkgs.openldap_libressl_cyrus;
      settings = {
        attrs = {
          olcPidFile = cfg.pids.pid;
          olcArgsFile = cfg.pids.args;
          olcLogLevel = "none";
          olcTLSCertificateFile = "${config.security.acme.certs.ldap.directory}/cert.pem";
          olcTLSCertificateKeyFile = "${config.security.acme.certs.ldap.directory}/key.pem";
          olcTLSCACertificateFile = "${config.security.acme.certs.ldap.directory}/fullchain.pem";
          olcTLSCACertificatePath = "${pkgs.cacert.unbundled}/etc/ssl/certs/";
          # This makes openldap crash
          # olcTLSCipherSuite = "DEFAULT";
          #olcSaslHost = "kerberos.immae.eu";
          # Map sasl "dn" to ldap dn
          #olcAuthzRegexp = ''{0}"uid=([^,]*)(,cn=IMMAE.EU)?,cn=(gssapi|gss-spnego),cn=auth" "uid=$1,ou=users,dc=immae,dc=eu"'';
        };
        children = {
          "cn=module{0}" = {
            attrs = {
              cn = "module{0}";
              objectClass = [ "olcModuleList" ];
              olcModuleLoad = [ "{0}back_mdb" "{1}memberof" "{2}syncprov" ];
            };
          };
          "cn=schema".includes = map (schema:
            "${config.services.openldap.package}/etc/schema/${schema}.ldif"
            ) [ "core" "cosine" "inetorgperson" "nis" ] ++ [
              "${openldap.immae-ldif}"
            ];
          "olcDatabase={0}config" = {
            attrs = {
              objectClass = "olcDatabaseConfig";
              olcDatabase = "{0}config";
              olcAccess = ["{0}to *  by * none"];
            };
          };
          "olcDatabase={1}mdb" = {
            attrs = {
              objectClass = [ "olcDatabaseConfig" "olcMdbConfig" ];
              olcDatabase = "{1}mdb";
              olcDbIndex = [
                "objectClass      eq"
                "uid              pres,eq"
                "mail             pres,eq,sub"
                "cn               pres,eq,sub"
                "sn               pres,eq,sub"
                "dc               eq"
                "member           eq"
                "memberOf         eq"
              ];
              olcAccess = let
                join = builtins.replaceStrings ["\n"] [" "];
              in [
                # First matching "to" + "by" wins
                #### Replication needs full access
                (join ''{0}to *
                  by dn.base="uid=ldap_replication,cn=ldap,ou=services,dc=immae,dc=eu" read
                  by * break
                '')
                #### Prevent modification of SASL passwords
                (join ''{1}to attrs=userPassword val.regex="^.SASL..+"
                  by self read
                  by anonymous auth
                  by * none
                '')
                #### Oneself needs access to users password
                (join ''{2}to attrs=userPassword,shadowLastChange
                  by self write
                  by anonymous auth
                  by * none
                '')
                #### Should be write, but disabled during migration to psql
                (join ''{3}to attrs=immaeSshKey
                  by self read
                  by * break
                '')

                #### Anyone can auth, and I can see myself
                (join ''{4}to *
                  by self read
                  by anonymous auth
                  by * break
                '')

                #### Specific access for phpldapadmin
                (join ''{5}to filter="(uid=*)"  attrs=entry,uid
                  by dn.base="cn=phpldapadmin,ou=services,dc=immae,dc=eu" read
                  by * break
                '')

                #### Hosts
                # The attributes are available to every host
                (join ''{6}to dn.one="ou=hosts,dc=immae,dc=eu"
                  by dn.subtree="ou=hosts,dc=immae,dc=eu" read
                  by dn.base="dc=immae,dc=eu" search
                  by * break
                '')
                #### /Hosts

                #### Local services
                # this/-* & user : all your ancestors have access to you
                # this/memberOf/-* & user : all those whom you belong to (in a group),
                #                           and their ancestors, have access to you
                # user/immaeAccessWriteDn*/member & this : you have write access to the
                #                                          members of your immaeAccessDn
                #                                          attributes
                # user/immaeAccessDn*/member & this : you have access to the members
                #                                        of your immaeAccessDn attributes
                # user/immaeAccessReadSubtree* & this/-* : you have access to the
                #                                         childrens of your immaeAccessReadSubtree
                #                                         attributes
                # this/memberOf/-* & user/immaeAccessReadSubtree*: you have access to
                #                                                  the members of the childrens of your 
                #                                                  immaeAccessReadSubtree attributes
                # http://www.openldap.org/faq/data/cache/1133.html
                (join ''{7}to dn.subtree="dc=immae,dc=eu"
                  by dn.subtree="ou=external_services,dc=immae,dc=eu" break
                  by set.exact="this/-* & user" read
                  by set.exact="this/memberOf/-* & user" read
                  by set.exact="user/immaeAccessWriteDn*/member & this" write
                  by set.exact="user/immaeAccessDn*/member & this" read
                  by set.exact="user/immaeAccessReadSubtree* & this/-*" read
                  by set.exact="this/memberOf/-* & user/immaeAccessReadSubtree*" read
                  by users search
                  by * break
                '')
                #### /Local services

                #### External services
                # http://www.openldap.org/faq/data/cache/429.html
                # FIXME: Find a way to whitelist?
                (join ''{8}to attrs=immaeSshKey
                  by dn.subtree="ou=external_services,dc=immae,dc=eu" none
                '')
                (join ''{9}to dn.subtree="dc=immae,dc=eu"
                  by set.exact="this/-* & user" read
                  by set.exact="this/memberOf/-* & user" read
                  by set.exact="user/immaeAccessDn*/member & this/-*" read
                  by users search
                  by * none
                '')
                  #### /External services
              ];
              olcDbDirectory = cfg.dataDir;
              olcRootDN = cfg.rootDn;
              olcRootPW.path = config.secrets.fullPaths."ldap/password";
              olcSuffix = cfg.baseDn;
            };
            children = {
              "olcOverlay={0}memberof" = {
                attrs = {
                  objectClass = [ "olcOverlayConfig" "olcMemberOf" ];
                  olcOverlay = "{0}memberof";
                };
              };
              "olcOverlay={1}syncprov" = {
                attrs = {
                  objectClass = [ "olcOverlayConfig" "olcSyncProvConfig" ];
                  olcOverlay = "{1}syncprov";
                  olcSpCheckpoint = "100 10";
                };
              };
            };
          };
        };
      };
    };
    myServices.monitoring.fromMasterActivatedPlugins = [ "tcp" ];
    myServices.monitoring.fromMasterObjects.service = [
      {
        service_description = "ldap SSL is up to date";
        host_name = config.hostEnv.fqdn;
        use = "external-service";
        check_command = ["check_tcp_ssl" "636"];

        servicegroups = "webstatus-ssl";
        _webstatus_name = "LDAP";
        _webstatus_url = "ldap.immae.eu";
      }
    ];
  };
}