Initial re-checki
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
{ lib, config, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
hostOpts = { config, ... }: {
|
||||
options = {
|
||||
ipv6Address = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The V6 IP of a given host, if any.
|
||||
'';
|
||||
};
|
||||
|
||||
ipv4Address = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The V4 IP of a given host, if any.
|
||||
'';
|
||||
};
|
||||
|
||||
macAddress = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The MAC address of a given host, if desired for IP reservation.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
localNameServerOpts = { config, ... }: {
|
||||
options = {
|
||||
ipv6Address = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The V6 IP of a given host, if any.
|
||||
'';
|
||||
};
|
||||
|
||||
ipv4Address = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The V4 IP of a given host, if any.
|
||||
'';
|
||||
};
|
||||
|
||||
ipv4ReverseDomain = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The domain of the IPv4 address range for which this nameserver is responsible.
|
||||
|
||||
Eg: 0.10.in-addr.arpa
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
in {
|
||||
imports = [
|
||||
./fudo/ldap.nix
|
||||
];
|
||||
|
||||
options = {
|
||||
|
||||
fudo.localNetwork.hosts = mkOption {
|
||||
type = types.listOf (submodule hostOpts);
|
||||
default = {};
|
||||
description = ''
|
||||
A map of hostname => { host_attributes }.
|
||||
'';
|
||||
};
|
||||
|
||||
fudo.localNetwork.domain = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The domain to use for the local network.
|
||||
'';
|
||||
};
|
||||
|
||||
fudo.localNetwork.hostAliases = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = {};
|
||||
description = ''
|
||||
A mapping of hostAlias => hostName to use on the local network.
|
||||
'';
|
||||
};
|
||||
|
||||
fudo.localNetwork.localNameServer = mkOption {
|
||||
type = (submodule localNameServerOpts);
|
||||
description = ''
|
||||
The master nameserver of the local network.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,415 @@
|
||||
{ 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 = types.str;
|
||||
description = ''
|
||||
The path to the SSL CA cert used to sign the certificate.
|
||||
'';
|
||||
};
|
||||
|
||||
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}
|
||||
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
|
||||
by group.exact="cn=admin,ou=groups,${cfg.base}" write
|
||||
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
|
||||
'';
|
||||
|
||||
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}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
catLines = builtins.concatStringsSep "\n";
|
||||
|
||||
userOpts = { config, ... }: {
|
||||
options = {
|
||||
passwd = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
The password of a given user.
|
||||
'';
|
||||
};
|
||||
|
||||
databases = mkOption {
|
||||
type = types.attrsOf types.lines;
|
||||
default = {};
|
||||
description = ''
|
||||
A list of databases to which this user should have access.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
grantDatabaseAccess = username: database: ''
|
||||
GRANT CONNECT ON DATABASE ${database} TO USER ${username};
|
||||
GRANT SELECT,INSERT,UPDATE,DELETE ON ALL TABLES IN SCHEMA ${database} TO USER ${username};
|
||||
'';
|
||||
|
||||
createUserSql = username: userOpts: ''
|
||||
CREATE ROLE ${username} ENCRYPTED PASSWORD ${userOpts.passwd};
|
||||
${catLines (map (grantDatabaseAccess username) userOpts.databases)}
|
||||
'';
|
||||
|
||||
createDatabaseSql = database: dbOpts: ''
|
||||
CREATE DATABASE ${database};
|
||||
USE ${database};
|
||||
'';
|
||||
|
||||
dataPath = /srv + ("/" + config.networking.hostName);
|
||||
|
||||
in {
|
||||
|
||||
options = {
|
||||
fudo.postgresql = {
|
||||
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to enable the PostgreSQL server for Fudo services.
|
||||
'';
|
||||
};
|
||||
|
||||
databases = mkOption {
|
||||
type = types.attrsOf types.lines;
|
||||
default = {};
|
||||
description = ''
|
||||
A map of database_name => database_defn.
|
||||
'';
|
||||
};
|
||||
|
||||
users = mkOption {
|
||||
type = with types; attrsOf (submodule userOpts);
|
||||
default = {};
|
||||
description = ''
|
||||
A map of user_name => { user_attributes }.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# config = mkIf config.fudo.postgresql.enable
|
||||
|
||||
# environment = {
|
||||
|
||||
# systemPackages = with pkgs; [
|
||||
# postgresql_11_gssapi
|
||||
# ];
|
||||
|
||||
# etc = {
|
||||
# "postgresql/private/privkey.pem" = {
|
||||
# mode = "0400";
|
||||
# user = "postgres";
|
||||
# group = "postgres";
|
||||
# source = dataPath + "/certs/private/privkey.pem";
|
||||
# };
|
||||
|
||||
# "postgresql/cert.pem" = {
|
||||
# mode = "0444";
|
||||
# user = "postgres";
|
||||
# group = "postgres";
|
||||
# source = dataPath + "/certs/cert.pem";
|
||||
# };
|
||||
|
||||
# "postgresql/private/postgres.keytab" = {
|
||||
# mode = "0400";
|
||||
# user = "postgres";
|
||||
# group = "postgres";
|
||||
# source = dataPath + "/keytabs/postgres.keytab";
|
||||
# };
|
||||
# };
|
||||
# };
|
||||
|
||||
# services.postgresql = {
|
||||
# enable = true;
|
||||
# package = pkgs.postgresql_11_gssapi;
|
||||
# enableTCPIP = true;
|
||||
|
||||
# extraConfig = ''
|
||||
# krb_server_keyfile = '/etc/postgresql/private/postgres.keytab'
|
||||
|
||||
# ssl = true
|
||||
# ssl_cert_file = '/etc/postgresql/cert.pem'
|
||||
# ssl_key_file = '/etc/postgresql/private/privkey.pem'
|
||||
# '';
|
||||
|
||||
# authentication = ''
|
||||
# local all all ident
|
||||
|
||||
# # host-local
|
||||
# host all all 127.0.0.1/32 gss include_realm=0 krb_realm=FUDO.ORG
|
||||
# host all all ::1/128 gss include_realm=0 krb_realm=FUDO.ORG
|
||||
|
||||
# # local network
|
||||
# host all all 10.0.0.1/24 gss include_realm=0 krb_realm=FUDO.ORG
|
||||
# host all all 2601:600:997f:fc00::/60 gss include_realm=0 krb_realm=FUDO.ORG
|
||||
# '';
|
||||
|
||||
# initialScript = pkgs.writeText "backend-initscript" ''
|
||||
# ${catLines (map createUserSql fudo.postgresql.users)}
|
||||
# ${catLines (map createDatabaseSql fudo.postgresql.databases)}
|
||||
# '';
|
||||
# };
|
||||
}
|
||||
Reference in New Issue
Block a user