{ 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";
}
];
};
}