Initial re-checki

This commit is contained in:
root
2019-12-25 17:20:36 -06:00
commit 46c45f4440
33 changed files with 2646 additions and 0 deletions
+95
View File
@@ -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.
'';
};
};
}
+415
View File
@@ -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}
'';
};
};
}
+138
View File
@@ -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)}
# '';
# };
}