421 lines
11 KiB
Nix
421 lines
11 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
let
|
|
|
|
cfg = config.fudo.auth.server;
|
|
|
|
ldapSystemUserOpts = { name, ... }: {
|
|
options = {
|
|
description = mkOption {
|
|
type = types.str;
|
|
description = ''
|
|
The description of this system user.
|
|
'';
|
|
};
|
|
|
|
hashed-password = mkOption {
|
|
type = types.str;
|
|
description = ''
|
|
The password for this user, hashed with ldappasswd.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
ldapGroupOpts = { name, ... }: {
|
|
options = {
|
|
gid = mkOption {
|
|
type = types.int;
|
|
description = ''
|
|
The GID number of this group.
|
|
'';
|
|
};
|
|
|
|
description = mkOption {
|
|
type = types.str;
|
|
description = ''
|
|
The description of this group.
|
|
'';
|
|
};
|
|
|
|
members = mkOption {
|
|
type = with types; listOf str;
|
|
default = [];
|
|
description = ''
|
|
A list of usernames representing the members of this group.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
ldapUserOpts = { name, ... }: {
|
|
options = {
|
|
|
|
uid = mkOption {
|
|
type = types.int;
|
|
description = ''
|
|
The UID number of this user.
|
|
'';
|
|
};
|
|
|
|
common-name = mkOption {
|
|
type = types.str;
|
|
description = ''
|
|
The given name of this user.
|
|
'';
|
|
};
|
|
|
|
group = mkOption {
|
|
type = types.str;
|
|
description = ''
|
|
The name of the user's primary group.
|
|
'';
|
|
};
|
|
|
|
login-shell = mkOption {
|
|
type = types.str;
|
|
default = "/bin/bash";
|
|
description = ''
|
|
The user's preferred shell. Default is /bin/bash.
|
|
'';
|
|
};
|
|
|
|
description = mkOption {
|
|
type = types.str;
|
|
default = "Fudo Member";
|
|
description = ''
|
|
The description of this user.
|
|
'';
|
|
};
|
|
|
|
hashed-password = mkOption {
|
|
type = types.str;
|
|
description = ''
|
|
The password for this user, hashed with ldappasswd.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
stringJoin = joiner: attrList:
|
|
if (length attrList) == 0 then
|
|
""
|
|
else
|
|
foldr(lAttr: rAttr: "${lAttr}${joiner}${rAttr}") (last attrList) (init attrList);
|
|
|
|
getUserGidNumber = user: group-map: group-map.${user.group}.gid;
|
|
|
|
attrOr = attrs: attr: value:
|
|
if attrs ? ${attr} then attrs.${attr} else value;
|
|
|
|
mkHomeDir = username: user-opts:
|
|
if (user-opts.group == "admin") then
|
|
"/home/${username}"
|
|
else
|
|
"/home/${user-opts.group}/${username}";
|
|
|
|
|
|
userLdif = base: name: group-map: opts: ''
|
|
dn: uid=${name},ou=members,${base}
|
|
uid: ${name}
|
|
objectClass: account
|
|
objectClass: shadowAccount
|
|
objectClass: posixAccount
|
|
cn: ${opts.common-name}
|
|
uidNumber: ${toString(opts.uid)}
|
|
gidNumber: ${toString(getUserGidNumber opts group-map)}
|
|
homeDirectory: ${mkHomeDir name opts}
|
|
description: ${opts.description}
|
|
shadowLastChange: 12230
|
|
shadowMax: 99999
|
|
shadowWarning: 7
|
|
userPassword: ${opts.hashed-password}
|
|
'';
|
|
|
|
systemUserLdif = base: name: opts: ''
|
|
dn: cn=${name},${base}
|
|
objectClass: organizationalRole
|
|
objectClass: simpleSecurityObject
|
|
cn: ${name}
|
|
description: ${opts.description}
|
|
userPassword: ${opts.hashed-password}
|
|
'';
|
|
|
|
toMemberList = userList:
|
|
stringJoin "\n" (map (username: "memberUid: ${username}") userList);
|
|
|
|
groupLdif = base: name: opts: ''
|
|
dn: cn=${name},ou=groups,${base}
|
|
objectClass: posixGroup
|
|
cn: ${name}
|
|
gidNumber: ${toString(opts.gid)}
|
|
description: ${opts.description}
|
|
${toMemberList opts.members}
|
|
'';
|
|
|
|
systemUsersLdif = base: user-map:
|
|
stringJoin "\n" (mapAttrsToList (name: opts:
|
|
systemUserLdif base name opts
|
|
) user-map);
|
|
|
|
groupsLdif = base: group-map:
|
|
stringJoin "\n" (mapAttrsToList (name: opts:
|
|
groupLdif base name opts
|
|
) group-map);
|
|
|
|
usersLdif = base: group-map: user-map:
|
|
stringJoin "\n" (mapAttrsToList (name: opts:
|
|
userLdif base name group-map opts
|
|
) user-map);
|
|
|
|
in {
|
|
|
|
options = {
|
|
fudo = {
|
|
auth = {
|
|
server = {
|
|
enable = mkEnableOption "Fudo Authentication";
|
|
|
|
kerberos-host = mkOption {
|
|
type = types.str;
|
|
description = ''
|
|
The name of the host to use for Kerberos authentication.
|
|
'';
|
|
};
|
|
|
|
kerberos-keytab = mkOption {
|
|
type = types.str;
|
|
description = ''
|
|
The path to a keytab for the LDAP server, containing a principal for ldap/<hostname>.
|
|
'';
|
|
};
|
|
|
|
sslCert = mkOption {
|
|
type = types.str;
|
|
description = ''
|
|
The path to the SSL certificate to use for the server.
|
|
'';
|
|
};
|
|
|
|
sslKey = mkOption {
|
|
type = types.str;
|
|
description = ''
|
|
The path to the SSL key to use for the server.
|
|
'';
|
|
};
|
|
|
|
sslCACert = mkOption {
|
|
type = with types; nullOr str;
|
|
description = ''
|
|
The path to the SSL CA cert used to sign the certificate.
|
|
'';
|
|
default = null;
|
|
};
|
|
|
|
organization = mkOption {
|
|
type = types.str;
|
|
description = ''
|
|
The name to use for the organization.
|
|
'';
|
|
};
|
|
|
|
base = mkOption {
|
|
type = types.str;
|
|
description = ''
|
|
The base dn of the LDAP server (eg. "dc=fudo,dc=org").
|
|
'';
|
|
};
|
|
|
|
rootpw-file = mkOption {
|
|
default = "";
|
|
type = types.str;
|
|
description = ''
|
|
The path to a file containing the root password for this database.
|
|
'';
|
|
};
|
|
|
|
listen-uris = mkOption {
|
|
default = [];
|
|
type = with types; listOf str;
|
|
description = ''
|
|
A list of URIs on which the ldap server should listen.
|
|
'';
|
|
example = [
|
|
"ldap://auth.fudo.org"
|
|
"ldaps://auth.fudo.org"
|
|
];
|
|
};
|
|
|
|
users = mkOption {
|
|
default = {};
|
|
type = with types; loaOf (submodule ldapUserOpts);
|
|
example = {
|
|
tester = {
|
|
uid = 10099;
|
|
common-name = "Joe Blow";
|
|
hashed-password = "<insert password hash>";
|
|
};
|
|
};
|
|
description = ''
|
|
Users to be added to the Fudo LDAP database.
|
|
'';
|
|
};
|
|
|
|
groups = mkOption {
|
|
default = {};
|
|
type = with types; loaOf (submodule ldapGroupOpts);
|
|
example = {
|
|
admin = {
|
|
gid = 1099;
|
|
members = [
|
|
"tester"
|
|
];
|
|
};
|
|
};
|
|
description = ''
|
|
Groups to be added to the Fudo LDAP database.
|
|
'';
|
|
};
|
|
|
|
system-users = mkOption {
|
|
default = {};
|
|
type = with types; loaOf (submodule ldapSystemUserOpts);
|
|
example = {
|
|
replicator = {
|
|
description = "System user for database sync";
|
|
hashed-password = "<insert password hash>";
|
|
};
|
|
};
|
|
description = ''
|
|
System users to be added to the Fudo LDAP database.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
|
|
environment = {
|
|
etc = {
|
|
"openldap/sasl2/slapd.conf" = {
|
|
mode = "0400";
|
|
user = "openldap";
|
|
group = "openldap";
|
|
text = ''
|
|
mech_list: gssapi external
|
|
keytab: /etc/ldap/ldap.keytab
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
systemd.services.openldap = {
|
|
environment = {
|
|
KRB5_KTNAME = cfg.kerberos-keytab;
|
|
};
|
|
};
|
|
|
|
services.openldap = {
|
|
|
|
enable = true;
|
|
suffix = cfg.base;
|
|
rootdn = "cn=admin,${cfg.base}";
|
|
rootpwFile = "${cfg.rootpw-file}";
|
|
urlList = cfg.listen-uris;
|
|
|
|
extraConfig = ''
|
|
|
|
TLSCertificateFile ${cfg.sslCert}
|
|
TLSCertificateKeyFile ${cfg.sslKey}
|
|
${optionalString (cfg.sslCACert != null) "TLSCACertificateFile ${cfg.sslCACert}"}
|
|
|
|
authz-regexp "^uid=auth/([^.]+)\.fudo\.org,cn=fudo\.org,cn=gssapi,cn=auth$" "cn=$1,ou=hosts,dc=fudo,dc=org"
|
|
authz-regexp "^uid=[^,/]+/root,cn=fudo\.org,cn=gssapi,cn=auth$" "cn=admin,dc=fudo,dc=org"
|
|
authz-regexp "^uid=([^,/]+),cn=fudo\.org,cn=gssapi,cn=auth$" "uid=$1,ou=members,dc=fudo,dc=org"
|
|
authz-regexp "^uid=host/([^,/]+),cn=fudo\.org,cn=gssapi,cn=auth$" "cn=$1,ou=hosts,dc=fudo,dc=org"
|
|
authz-regexp "^gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth$" "cn=admin,dc=fudo,dc=org"
|
|
|
|
'';
|
|
|
|
extraDatabaseConfig = ''
|
|
# access to dn=base=""
|
|
# by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage
|
|
# by * read
|
|
|
|
access to attrs=userPassword,shadowLastChange
|
|
by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage
|
|
by group.exact="cn=admin,ou=members,${cfg.base}" write
|
|
by dn.exact="cn=auth_reader,${cfg.base}" read
|
|
by dn.exact="cn=replicator,${cfg.base}" read
|
|
by self write
|
|
by * auth
|
|
|
|
access to dn.exact="cn=admin,ou=groups,${cfg.base}"
|
|
by dn.exact="cn=admin,${cfg.base}" write
|
|
by users read
|
|
by * none
|
|
|
|
access to dn.subtree="ou=groups,${cfg.base}" attrs=memberUid
|
|
by dn.regex="cn=[a-zA-Z][a-zA-Z0-9_]+,ou=hosts,${cfg.base}" write
|
|
by group.exact="cn=admin,ou=groups,${cfg.base}" write
|
|
by users read
|
|
by * none
|
|
|
|
access to dn.subtree="ou=members,${cfg.base}" attrs=cn,sn,homeDirectory,loginShell,gecos,description,homeDirectory,uidNumber,gidNumber
|
|
by group.exact="cn=admin,ou=groups,${cfg.base}" write
|
|
by dn.exact="cn=user_db_reader,${cfg.base}" read
|
|
by users read
|
|
by * none
|
|
|
|
access to dn.exact="cn=admin,ou=groups,${cfg.base}"
|
|
by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage
|
|
by users read
|
|
by * none
|
|
|
|
access to dn.subtree="ou=groups,${cfg.base}" attrs=memberUid
|
|
by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage
|
|
by dn.regex="cn=[a-zA-Z][a-zA-Z0-9_]+,ou=hosts,${cfg.base}" write
|
|
by group.exact="cn=admin,ou=groups,${cfg.base}" write
|
|
by users read
|
|
by * none
|
|
|
|
access to *
|
|
by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage
|
|
by users read
|
|
by * none
|
|
|
|
|
|
index objectClass,uid eq
|
|
'';
|
|
|
|
declarativeContents = ''
|
|
dn: ${cfg.base}
|
|
objectClass: top
|
|
objectClass: dcObject
|
|
objectClass: organization
|
|
o: ${cfg.organization}
|
|
|
|
dn: ou=groups,${cfg.base}
|
|
objectClass: organizationalUnit
|
|
description: ${cfg.organization} groups
|
|
|
|
dn: ou=members,${cfg.base}
|
|
objectClass: organizationalUnit
|
|
description: ${cfg.organization} members
|
|
|
|
dn: cn=admin,${cfg.base}
|
|
objectClass: organizationalRole
|
|
cn: admin
|
|
description: "Admin User"
|
|
|
|
${systemUsersLdif cfg.base cfg.system-users}
|
|
${groupsLdif cfg.base cfg.groups}
|
|
${usersLdif cfg.base cfg.groups cfg.users}
|
|
'';
|
|
};
|
|
};
|
|
}
|