Move profiles from ./profile-config to ./profile

This commit is contained in:
niten 2021-11-02 09:34:26 -07:00
parent 79b05be7d3
commit 566643e195
48 changed files with 1490 additions and 997 deletions

View File

@ -8,6 +8,7 @@
./groups.nix ./groups.nix
./hosts.nix ./hosts.nix
./networks.nix ./networks.nix
./profile.nix
./sites.nix ./sites.nix
./users.nix ./users.nix
./wireless-networks.nix ./wireless-networks.nix

View File

@ -49,7 +49,7 @@
hardware.bluetooth.enable = false; hardware.bluetooth.enable = false;
network = { networking = {
macvlans = { macvlans = {
intif0 = { intif0 = {
interface = "enp4s0f1"; interface = "enp4s0f1";

View File

@ -10,10 +10,17 @@ let
host-fqdn = "${hostname}.${domain-name}"; host-fqdn = "${hostname}.${domain-name}";
mail-hostname = "mail.fudo.org"; mail-hostname = "mail.fudo.org";
france-secrets = config.fudo.secrets.host-secrets.france; secrets = config.fudo.secrets.host-secrets.france;
secret-files = config.fudo.secrets.files;
acme-private-key = hostname: "/var/lib/acme/${hostname}/key.pem"; letsencrypt-full-chain = name: chain: pkgs.stdenv.mkDerivation {
acme-certificate = hostname: "/var/lib/acme/${hostname}/fullchain.pem"; name = "${name}-letsencrypt-full-chain.pem";
phases = "installPhase";
installPhase = ''
cat ${chain} > $out
cat ${pkgs.letsencrypt-ca}/ca.pem >> $out
'';
};
in { in {
imports = let imports = let
@ -28,9 +35,66 @@ in {
in nix-files ./france; in nix-files ./france;
config = { config = {
security.acme.email = "admin@fudo.org";
fudo = { fudo = {
hosts.france.external-interfaces = [ "extif0" ]; hosts.france.external-interfaces = [ "extif0" ];
acme.host-domains.france."france.fudo.org" = {
email = "admin@fudo.org";
local-copies = {
postgres = {
user = config.services.postgresql.user;
};
openldap = {
user = config.services.openldap.user;
};
};
};
secrets.host-secrets.${hostname} = let
ldap-user = config.services.openldap.user;
ldap-group = config.services.openldap.group;
in {
ldap-ssl-certificate = {
source-file = cfg.ssl-certificate;
target-file = "/run/openldap/ssl-certificate.pem";
user = ldap-user;
group = ldap-group;
permissions = "0444";
};
ldap-ssl-private-key = {
source-file = cfg.ssl-private-key;
target-file = "/run/openldap/ssl-private-key.pem";
user = ldap-user;
group = ldap-group;
};
ldap-ssl-ca-certificate = {
source-file = cfg.ssl-ca-certificate;
target-file = "/run/openldap/ssl-ca-certificate.pem";
user = ldap-user;
group = ldap-group;
permissions = "0444";
};
ldap-keytab = {
source-file = secret-files.service-keytabs.france.ldap;
target-file = "/run/openldap/ldap.keytab";
user = ldap-user;
group = ldap-group;
};
ldap-root-passwd = {
source-file = passwd.random-passwd-file;
target-file = "/run/openldap/root.passwd";
user = ldap-user;
group = ldap-group;
};
postgres-keytab = {
source-file = secret-files.service-keytabs.france.postgres;
target-file = "/run/postgres/postgres.keytab";
user = config.services.postgresql.user;
};
};
client.dns = { client.dns = {
enable = true; enable = true;
ipv4 = true; ipv4 = true;
@ -40,16 +104,53 @@ in {
}; };
france = { france = {
ldap = let
cert-copy = config.fudo.acme.host-domains.france."france.fudo.org".local-copies.openldap;
chain = "${letsencrypt-full-chain "openldap-france" cert-copy.chain}";
in {
ssl-certificate = cert-copy.certificate;
ssl-private-key = cert-copy.private-key;
ssl-ca-certificate = chain;
keytab = secrets.ldap-keytab.target-file;
root-password-file = secrets.ldap-root-passwd.target-file;
};
kdc = {
state-directory = "/state/kerberos";
master-key-file = "";
listen-ips = [ primary-ip "127.0.0.1" "127.0.1.1" "::1" ];
};
jabber = {
ldap-servers = [ "france.fudo.org" ];
listen-ips = [ primary-ip ];
};
backplane = {
host-passwd-files = let
hosts = attrNames config.fudo.hosts;
in mapAttrs (hostname: hostOpts: hostOpts.backplane-password-file)
config.fudo.hosts;
service-passwd-files = genAttrs [ "dns" ]
(service-name:
lib.fudo.passwd.stablerandom-passwd-file
"${service-name}-service-backplane-passwd"
"${service-name}-service-backplane-passwd-${config.instance.build-seed}");
};
backplane-server = {
listen-ips = [ primary-ip ];
};
mail = { mail = {
mail-directory = "/state/mail-server/mail"; mail-directory = "/srv/mail/mailboxes";
state-directory = "/state/mail-server/var"; state-directory = "/srv/mail/var";
ldap-server-urls = [ ldap-server-urls = [
"ldap://france.fudo.org" "ldap://france.fudo.org"
]; ];
}; };
webmail = { webmail = {
# TODO: this is not using the database!
mail-server = mail-hostname; mail-server = mail-hostname;
database.hostname = "localhost"; database.hostname = "localhost";
}; };
@ -58,6 +159,15 @@ in {
repository-directory = "/state/gitea/repo"; repository-directory = "/state/gitea/repo";
state-directory = "/state/gitea/state"; state-directory = "/state/gitea/state";
ssh.listen-ip = git-server-ip; ssh.listen-ip = git-server-ip;
database-host = "localhost";
};
postgresql = let
cert-copy = config.fudo.acme.host-domains.france."france.fudo.org".local-copies.postgres;
in {
keytab = secrets.postgres-keytab.target-file;
ssl-certificate = cert-copy.certificate;
ssl-private-key = cert-copy.private-key;
}; };
}; };
@ -71,6 +181,9 @@ in {
}; };
networking = { networking = {
useDHCP = false;
interfaces = {
intif0 = { intif0 = {
ipv4.addresses = [{ ipv4.addresses = [{
address = "192.168.11.1"; address = "192.168.11.1";
@ -90,12 +203,13 @@ in {
]; ];
}; };
}; };
};
services = { services = {
nginx = { nginx = {
enable = true; enable = true;
recommendedGzipSettings = true; recommendedGzipSettings = true;
recommendedOptimisations = true; recommendedOptimisation = true;
recommendedTlsSettings = true; recommendedTlsSettings = true;
recommendedProxySettings = true; recommendedProxySettings = true;

View File

@ -7,83 +7,79 @@ let
site-name = config.instance.local-site; site-name = config.instance.local-site;
fqdn = "${hostname}.${domain-name}"; fqdn = "${hostname}.${domain-name}";
secrets = config.fudo.secrets.host-secrets.france;
# same as genAttr, but takes back attrsets and merges them # same as genAttr, but takes back attrsets and merges them
concatGenAttrs = lst: f: concatGenAttrs = lst: f:
foldr (a0: a1: a0 // a1) {} (map f lst); foldr (a0: a1: a0 // a1) {} (map f lst);
passwd = import ../../../lib/passwd.nix { inherit lib; };
secrets = config.fudo.secrets.host-secrets.${hostname};
cfg = config.fudo.france;
in { in {
options.france = with types; { options.fudo.france = with types; {
ldap = { ldap = {
ssl-certificate = mkOption { ssl-certificate = mkOption {
type = path; type = str;
description = "SSL certificate to use for the LDAP server."; description = "SSL certificate to use for the LDAP server.";
}; };
ssl-private-key = mkOption { ssl-private-key = mkOption {
type = path; type = str;
description = "SSL private key to use for the LDAP server."; description = "SSL private key to use for the LDAP server.";
}; };
ssl-ca-certificate = mkOption { ssl-ca-certificate = mkOption {
type = path; type = str;
description = "SSL certificate authority to use for the LDAP server."; description = "SSL certificate authority to use for the LDAP server.";
}; };
keytab = mkOption {
type = str;
description = "Path to the LDAP service keytab.";
};
root-password-file = mkOption {
type = str;
description = "Path to the file containing the LDAP root password.";
};
}; };
kdc = { kdc = {
state-directory = mkOption { state-directory = mkOption {
type = str; type = str;
description = "Path at which to store kerberos state."; description = "Path at which to store kerberos state.";
default = "/state/kerberos";
}; };
master-key-file = mkOption { master-key-file = mkOption {
type = str; type = str;
description = "Heimdal database master key file."; description = "Heimdal database master key file.";
}; };
listen-ips = mkOption {
type = listOf str;
description = "IP addresses on which to listen for connections.";
};
}; };
}; };
config = { config = {
fudo = { fudo = {
secrets.host-secrets.${hostname} = { secrets.host-secrets.${hostname}.kdc-master-key = {
ldap-ssl-certificate = { source-file = cfg.kdc.master-key-file;
source-file = cfg.ssl-certificate; target-file = "/run/kerberos/kdc/master.key";
target-file = "/var/run/ldap/ssl-certificate.pem"; user = config.fudo.auth.kdc.user;
user = config.services.openldap.user;
group = config.services.openldap.group;
permissions = "0444";
};
ldap-ssl-private-key = {
source-file = cfg.ssl-private-key;
target-file = "/var/run/ldap/ssl-private-key.pem";
user = config.services.openldap.user;
group = config.services.openldap.group;
permissions = "0400";
};
ldap-ssl-ca-certificate = {
source-file = cfg.ssl-ca-certificate;
target-file = "/var/run/ldap/ssl-ca-certificate.pem";
user = config.services.openldap.user;
group = config.services.openldap.group;
permissions = "0400";
};
}; };
auth = { auth = {
ldap = { ldap-server = {
enable = true; enable = true;
base = "dc=fudo,dc=org"; base = "dc=fudo,dc=org";
organization = "Fudo"; organization = "Fudo";
rootpw-file = secrets.ldap-root-passwd; rootpw-file = cfg.ldap.root-password-file;
kerberos-host = fqdn; kerberos-host = fqdn;
kerberos-keytab = secrets.ldap-keytab; kerberos-keytab = cfg.ldap.keytab;
ssl-certificate = cfg.ldap.ssl-certificate;
sslCert = ssl-private-key = cfg.ldap.ssl-private-key;
secrets.ldap-ssl-certificate.target-file; ssl-ca-certificate = cfg.ldap.ssl-ca-certificate;
sslKey =
secrets.ldap-ssl-private-key.target-file;
sslCACert =
secrets.ldap-ssl-ca-certificate.target-file;
listen-uris = [ "ldap:///" "ldaps:///" "ldapi:///" ]; listen-uris = [ "ldap:///" "ldaps:///" "ldapi:///" ];
@ -95,9 +91,9 @@ in {
# TODO: let build hosts create keys? # TODO: let build hosts create keys?
kdc = { kdc = {
enable = true; enable = true;
realm = config.domains.${domain-name}.gssapi-realm; realm = config.fudo.domains.${domain-name}.gssapi-realm;
state-directory = cfg.state-directory; state-directory = cfg.kdc.state-directory;
master-key-file = cfg.master-key-file; master-key-file = secrets.kdc-master-key.target-file;
acl = let acl = let
admin-entries = concatGenAttrs admin-entries = concatGenAttrs
config.instance.local-admins config.instance.local-admins
@ -109,7 +105,7 @@ in {
"host/*.fudo.org" = { perms = [ "add" ]; }; "host/*.fudo.org" = { perms = [ "add" ]; };
"pam_migrate/*.fudo.org" = { perms = [ "add" "change-password" ]; }; "pam_migrate/*.fudo.org" = { perms = [ "add" "change-password" ]; };
} // admin-entries; } // admin-entries;
bind-addresses = [ primary-ip "127.0.0.1" "127.0.1.1" "::1" ]; bind-addresses = cfg.kdc.listen-ips;
}; };
}; };
}; };

View File

@ -0,0 +1,148 @@
{ config, lib, pkgs, ... }:
with lib;
let
hostname = config.instance.hostname;
timestamp = config.instance.build-timestamp;
domain = config.instance.local-domain;
powerdns-user = "backplane-powerdns";
backplane-dns-user = "backplane-dns";
generate-role-passwd = role:
lib.fudo.passwd.stablerandom-password-file
"backplane-${role}-password"
"${hostname}-${domain}-${role}-password-${config.instance.build-timestamp}";
powerdns-password = generate-role-passwd "powerdns-db";
backplane-dns-xmpp-password = generate-role-passwd "backplane-dns-xmpp";
backplane-dns-db-password = generate-role-passwd "backplane-dns-db";
secrets = config.fudo.secrets.host-secrets.france;
cfg = config.fudo.france.backplane-server;
in {
options.fudo.france.backplane-server = with types; {
listen-ips = mkOption {
type = listOf str;
description = "List of IPs on which to listen for incoming backplane connections.";
};
listen-ipv6s = mkOption {
type = listOf str;
description = "List of IPv6s on which to listen for incoming backplane connections.";
default = [];
};
};
config = {
users = {
users = {
${powerdns-user} = {
isSystemUser = true;
};
${backplane-dns-user} = {
isSystemUser = true;
};
};
groups = {
${powerdns-user} = {
members = [ powerdns-user ];
};
${backplane-dns-user} = {
members = [ backplane-dns-user ];
};
};
};
fudo = {
secrets.host-secrets.france = {
powerdns-password = {
source-file = powerdns-password;
target-file = "/run/backplane/dns/powerdns/db.passwd";
user = config.fudo.backplane.dns.database.user;
};
backplane-dns-db-password = {
source-file = backplane-dns-db-password;
target-file = "/run/backplane/dns/db.passwd";
user = config.fudo.backplane.dns.backplane.user;
};
backplane-dns-xmpp-password = {
source-file = backplane-dns-db-password;
target-file = "/run/backplane/dns/xmpp.passwd";
user = config.fudo.backplane.dns.backplane.user;
};
};
postgresql = {
enable = true;
required-services = [ "fudo-passwords.target" ];
users = {
${powerdns-user} = {
password-file = secrets.powerdns-password.target-file;
databases = {
backplane_dns = {
access = "CONNECT";
entity-access = {
"ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE";
"ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE";
};
};
};
};
${backplane-dns-user} = {
password-file = secrets.backplane-dns-db-password;
databases = {
backplane_dns = {
access = "CONNECT";
entity-access = {
"ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE";
"ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE";
};
};
};
};
};
databases = {
backplane_dns = {
users = ["niten"];
};
};
};
backplane.dns = {
enable = true;
listen-v4-addresses = cfg.listen-ips;
listen-v6-addresses = cfg.listen-ipv6s;
user = backplane-dns-user;
group = backplane-dns-user;
database = {
username = powerdns-user;
database = "backplane_dns";
# Uses an IP to avoid cyclical dependency...
host = "127.0.0.1";
password-file = secrets.powerdns-password.target-file;
};
backplane = {
host = "backplane.fudo.org";
role = "service-dns";
password-file = secrets.backplane-dns-xmpp-password.target-file;
database = {
username = backplane-dns-user;
database = backplane-dns-user;
host = "127.0.0.1";
password-file = secrets.backplane-dns-db-password.target-file;
};
};
};
};
};
}

View File

@ -7,14 +7,16 @@ let
secrets = config.fudo.secrets.host-secrets.${hostname}; secrets = config.fudo.secrets.host-secrets.${hostname};
cfg = config.fudo.france.git;
sshOpts = { ... }: { sshOpts = { ... }: {
options = { options = with types; {
listen-ip = mkOption { listen-ip = mkOption {
type = str; type = str;
description = "IP address on which to listen for SSH connections."; description = "IP address on which to listen for SSH connections.";
}; };
listen-port = mkOption { listen-port = mkOption {
type = str; type = port;
description = "Port on which to listen for SSH connections."; description = "Port on which to listen for SSH connections.";
default = 22; default = 22;
}; };
@ -23,7 +25,7 @@ let
in { in {
options.france.git = with types; { options.fudo.france.git = with types; {
repository-directory = mkOption { repository-directory = mkOption {
type = str; type = str;
description = "Path to store git repositories."; description = "Path to store git repositories.";
@ -43,6 +45,14 @@ in {
}; };
config.fudo = { config.fudo = {
secrets.host-secrets.${hostname}.git-database-password = {
source-file = lib.fudo.passwd.stablerandom-passwd-file
"gitea-database-passwd"
"${hostname}-gitea-database-passwd-${config.instance.build-seed}";
target-file = "/var/gitea/database.passwd";
user = config.services.gitea.user;
};
postgresql = { postgresql = {
databases.fudo_git.users = databases.fudo_git.users =
config.instance.local_admins; config.instance.local_admins;

View File

@ -5,23 +5,107 @@ let
hostname = config.instance.hostname; hostname = config.instance.hostname;
secrets = config.fudo.secrets.host-secrets.${hostname}; secrets = config.fudo.secrets.host-secrets.${hostname};
cfg = config.fudo.france;
generate-auth-file = name: files: let
make-entry = name: passwd-file:
''("${name}" . "${readFile passwd-file}")'';
entries = mapAttrsToList make-entry files;
content = concatStringsSep "\n" entries;
in writeText "${name}-backplane-auth.scm" "'(${content})'";
host-auth-file = generate-auth-file "host" cfg.host-passwd-files;
service-auth-file = generate-auth-filre "service" cfg.service-passwd-files;
ldap-password-file =
lib.fudo.passwd.random-passwd-file "ejabberd-ldap-auth-user";
ldap-hashed-password =
hash-ldap-passwd "ejabberd-ldap-hashed-passwd" ldap-password-file;
in { in {
options.fudo.france = with types; {
jabber = {
ldap-user = mkOption {
type = str;
description = "System user as which to authenticate to LDAP.";
default = "ejabberd";
};
ldap-servers = mkOption {
type = listOf str;
description = "LDAP servers to use for user authentication.";
};
listen-ips = mkOption {
type = listOf str;
description = "IPs on which to listen for incoming connections.";
};
};
backplane = {
host-passwd-files = mkOption {
type = attrsOf str;
description = "Map of hostname to password file, for backplane host authentication.";
default = {};
};
service-passwd-files = mkOption {
type = attrsOf str;
description = "Map of service to password file, for backplane service authentication.";
default = {};
};
};
};
config = { config = {
fudo = { fudo = {
system-users.${cfg.jabber.ldap-user} = {
description = "ejabberd authentication user.";
hashed-password = ldap-hashed-password;
};
secrets.host-secrets.${hostname} = let
user = config.services.ejabberd.user;
in {
host-auth = {
source-file = host-auth-file;
target-file = "/run/backplane/host-auth-file.scm";
user = user;
};
service-auth = {
source-file = service-auth-file;
target-file = "/run/backplane/service-auth-file.scm";
user = user;
};
ldap-password = {
source-file = ldap-password-file;
target-file = "/run/ejabberd/ldap.passwd";
user = user;
};
};
jabber = { jabber = {
enable = true; enable = true;
listen-ips = cfg.jabber.listen-ips;
environment = {
FUDO_HOST_PASSWD_FILE = secrets.host-auth.target-file;
FUDO_SERVICE_PASSWD_FILE = secrets.service-auth.target-file;
};
secret-files = { secret-files = {
LDAP_PASSWORD = secrets.jabber-ldap-password.target-file; LDAP_PASSWORD = secrets.ldap-password.target-file;
}; };
sites = { sites = {
"fudo.im" = { "fudo.im" = {
site-config = { site-config = {
auth_method = "ldap"; auth_method = "ldap";
ldap_servers = [ "auth.fudo.org" ]; ldap_servers = cfg.jabber.ldap-servers;
ldap_port = 389; ldap_port = 389;
ldap_rootdn = "cn=jabber,dc=fudo,dc=org"; ldap_rootdn = "cn=${cfg.jabber.ldap-user},dc=fudo,dc=org";
ldap_password = ''"LDAP_PASSWD"''; ldap_password = ''"LDAP_PASSWD"'';
ldap_base = "ou=members,dc=fudo,dc=org"; ldap_base = "ou=members,dc=fudo,dc=org";
ldap_filter = "(objectClass=posixAccount)"; ldap_filter = "(objectClass=posixAccount)";
@ -69,7 +153,7 @@ in {
"backplane.fudo.org" = { "backplane.fudo.org" = {
site-config = { site-config = {
auth_method = "external"; auth_method = "external";
extauth_program = "${pkgs.guile}/bin/guile -s ${backplane-auth}"; extauth_program = "${pkgs.guile}/bin/guile -s ${pkgs.backplane-auth}/backplane-auth.scm";
extauth_pool_size = 3; extauth_pool_size = 3;
auth_use_cache = true; auth_use_cache = true;
@ -100,7 +184,6 @@ in {
mod_time = {}; mod_time = {};
mod_version = {}; mod_version = {};
}; };
}; };
}; };
}; };

View File

@ -9,7 +9,7 @@ let
mail-reader-dn = "mail-auth-reader"; mail-reader-dn = "mail-auth-reader";
in { in {
options.france.mail = with types; { options.fudo.france.mail = with types; {
mail-directory = mkOption { mail-directory = mkOption {
type = str; type = str;
description = "Directory to contain user maildirs."; description = "Directory to contain user maildirs.";
@ -39,16 +39,19 @@ in {
enableContainer = true; enableContainer = true;
monitoring = true; monitoring = true;
hostname = "mail.${domain-name}"; domain = domain-name;
mail-hostname = "mail.${domain-name}";
state-directory = cfg.state-directory; dovecot = {
mail-directory = cfg.mail-directory; ldap = {
reader-dn = "cn=${mail-reader-dn},${config.fudo.auth.ldap.base}";
dovecot.ldap = {
reader-dn = "cn=mail-reader-dn,${config.fudo.auth.ldap.base}";
reader-password-file = secrets.mail-reader-passwd.target-file; reader-password-file = secrets.mail-reader-passwd.target-file;
server-urls = cfg.ldap-server-urls; server-urls = cfg.ldap-server-urls;
}; };
};
state-directory = cfg.state-directory;
mail-directory = cfg.mail-directory;
clamav.enable = true; clamav.enable = true;
dkim.signing = true; dkim.signing = true;

View File

@ -5,14 +5,28 @@ let
hostname = config.instance.hostname; hostname = config.instance.hostname;
secrets = config.fudo.secrets.host-secrets.${hostname}; secrets = config.fudo.secrets.host-secrets.${hostname};
in { in {
options.fudo.france.postgresql = with types; {
ssl-certificate = mkOption {
type = str;
description = "SSL certificate to use for the LDAP server.";
};
ssl-private-key = mkOption {
type = str;
description = "SSL private key to use for the LDAP server.";
};
keytab = mkOption {
type = path;
description = "Postgres service keytab.";
};
};
config.fudo.postgresql = { config.fudo.postgresql = {
enable = true; enable = true;
local-networks = config.instance.local-networks; local-networks = config.instance.local-networks;
admin-users = config.instance.admin-users;
ssl-private-key = secrets.postgres-ssl-key; ssl-private-key = cfg.ssl-private-key;
ssl-certificate = secrets.postgres-ssl-certificate; ssl-certificate = cfg.ssl-certificate;
keytab = secrets.postgres-keytab.target-file; keytab = cfg.keytab;
}; };
} }

View File

@ -17,7 +17,7 @@ let
db-passwd = pkgs.lib.fudo.passwd.random-passwd-file "webmail" 40; db-passwd = pkgs.lib.fudo.passwd.random-passwd-file "webmail" 40;
in { in {
options.france.webmail = with types; { options.fudo.france.webmail = with types; {
mail-server = mkOption { mail-server = mkOption {
type = str; type = str;
description = "Mail server to use for webmail."; description = "Mail server to use for webmail.";

View File

@ -22,6 +22,8 @@ let
secrets = config.fudo.secrets.host-secrets.procul; secrets = config.fudo.secrets.host-secrets.procul;
passwd = pkgs.lib.fudo.passwd;
in { in {
networking = { networking = {
dhcpcd.enable = false; dhcpcd.enable = false;
@ -85,75 +87,17 @@ in {
fudo = { fudo = {
hosts.procul.external-interfaces = [ "extif0" ]; hosts.procul.external-interfaces = [ "extif0" ];
jabber = {
enable = true;
secret-files = {
SECRET = secrets.jabber-ldap-password.traget-file;
};
sites."informis.land" = {
site-config = {
auth_method = "ldap";
ldap_servers = [ "auth.fudo.org" ];
ldap_port = 636;
ldap_rootdn = "cn=jabber,dc=fudo,dc=org";
ldap_password = ''"LDAP_PASSWD"'';
ldap_base = "ou=members,dc=fudo,dc=org";
ldap_filter = "(objectClass=posixAccount)";
ldap_uids = { uid = "%u"; };
modules = {
mod_adhoc = {};
mod_announce = {};
mod_avatar = {};
mod_blocking = {};
mod_caps = {};
mod_carboncopy = {};
mod_client_state = {};
mod_configure = {};
mod_disco = {};
mod_fail2ban = {};
mod_last = {};
mod_offline = {
access_max_user_messages = 5000;
};
mod_ping = {};
mod_privacy = {};
mod_private = {};
mod_pubsub = {
access_createnode = "pubsub_createnode";
ignore_pep_from_offline = true;
last_item_cache = false;
plugins = [
"flat"
"pep"
];
};
mod_roster = {};
mod_stream_mgmt = {};
mod_time = {};
mod_vcard = {
search = false;
};
mod_vcard_xupdate = {};
mod_version = {};
};
};
};
};
secrets.host-secrets.procul = let secrets.host-secrets.procul = let
secrets = config.fudo.secrets.files; files = config.fudo.secrets.files;
in { in {
postgres-keytab = { postgres-keytab = {
source-file = secrets.service-keytabs.procul.postgres; source-file = files.service-keytabs.procul.postgres;
target-file = "/srv/postgres/secure/postgres.keytab"; target-file = "/srv/postgres/secure/postgres.keytab";
user = "root"; user = "root";
}; };
gitea-database-password = { gitea-database-password = {
source-file = secrets.service-passwords.procul.gitea-database; source-file = files.service-passwords.procul.gitea-database;
target-file = "/srv/gitea/secure/database.passwd"; target-file = "/srv/gitea/secure/database.passwd";
user = config.fudo.git.user; user = config.fudo.git.user;
}; };

View File

@ -1,8 +1,18 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
with lib;
let let
syslib = pkgs.callPackage ../lib/hosts.nix {}; syslib = pkgs.callPackage ../lib/hosts.nix {};
in { in {
config.fudo.hosts = syslib.base-host-config ./hosts; config.fudo.hosts = let
build-seed = config.instance.build-seed;
base-config = syslib.base-host-config ./hosts;
in mapAttrs (hostname: base-config:
base-config // {
backplane-password-file =
pkgs.lib.fudo.passwd.stablerandom-passwd-file
"${hostname}-host-backplane-passwd"
"${hostname}-host-backplane-passwd-${build-seed}";
}) base-config;
} }

View File

@ -1,140 +1,4 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
with lib; {
let
hostname = config.instance.hostname;
enable-gui = config.fudo.hosts.${hostname}.enable-gui;
in {
imports = [ ./common.nix ];
boot.plymouth.enable = false;
boot.tmpOnTmpfs = true;
environment = mkIf enable-gui {
systemPackages = [
#libva
];
};
# We're deploying via nixops, this is just annoying
system = { autoUpgrade.enable = false; };
services = {
xserver = mkIf enable-gui {
enable = true;
desktopManager.gnome.enable = true;
displayManager.gdm = {
enable = true;
wayland = false;
};
windowManager.stumpwm.enable = true;
# windowManager.session = pkgs.lib.singleton {
# name = "stumpwm";
# start = ''
# ${pkgs.lispPackages.stumpwm}/bin/stumpwm &
# waidPID=$!
# '';
# };
};
trezord.enable = true;
};
hardware = {
bluetooth.enable = true;
opengl = mkIf enable-gui {
enable = true;
driSupport = true;
driSupport32Bit = true;
};
};
sound.enable = true;
hardware.pulseaudio = {
enable = true;
support32Bit = config.hardware.pulseaudio.enable;
};
console.font =
lib.mkDefault "${pkgs.terminus_font}/share/consolefonts/ter-g18n.psf.gz";
services.gnome = mkIf enable-gui {
evolution-data-server.enable = mkForce false;
gnome-user-share.enable = mkForce false;
};
services.flatpak.enable = enable-gui;
# programs.steam.enable = enable-gui;
fonts = mkIf enable-gui {
fontDir.enable = true;
fontconfig.enable = true;
#fontconfig.antialias = true;
#fontconfig.penultimate.enable = true;
#fontconfig.subpixel.lcdfilter = "default";
fonts = with pkgs; [
cantarell_fonts
dejavu_fonts
dina-font
dosemu_fonts
fira-code
fira-code-symbols
freefont_ttf
liberation_ttf
mplus-outline-fonts
nerdfonts
noto-fonts
noto-fonts-cjk
noto-fonts-emoji
proggyfonts
terminus_font
ubuntu_font_family
ucsFonts
ultimate-oldschool-pc-font-pack
unifont
xorg.fontadobe100dpi
xorg.fontadobe75dpi
xorg.fontadobeutopia100dpi
xorg.fontadobeutopia75dpi
xorg.fontadobeutopiatype1
xorg.fontarabicmisc
xorg.fontbh100dpi
xorg.fontbh75dpi
xorg.fontbhlucidatypewriter100dpi
xorg.fontbhlucidatypewriter75dpi
xorg.fontbhttf
xorg.fontbhtype1
xorg.fontbitstream100dpi
xorg.fontbitstream75dpi
xorg.fontbitstreamtype1
xorg.fontcronyxcyrillic
xorg.fontcursormisc
xorg.fontdaewoomisc
xorg.fontdecmisc
xorg.fontibmtype1
xorg.fontisasmisc
xorg.fontjismisc
xorg.fontmicromisc
xorg.fontmisccyrillic
xorg.fontmiscethiopic
xorg.fontmiscmeltho
xorg.fontmiscmisc
xorg.fontmuttmisc
xorg.fontschumachermisc
xorg.fontscreencyrillic
xorg.fontsonymisc
xorg.fontsunmisc
xorg.fontwinitzkicyrillic
xorg.fontxfree86type1
];
};
} }

View File

@ -1,142 +1,5 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
with lib; with lib;
let {
# Available to all users on the system. Keep it minimal.
global-packages = with pkgs; [
bind
cryptsetup
git
heimdal
openssh_gssapi
tldr
vim
wget
];
in {
environment = {
etc.nixos-live.source = ../../.;
systemPackages = global-packages;
# shellInit = ''
# ${pkgs.gnupg}/bin/gpg-connect-agent /bye
# export SSH_AUTH_SOCK=$(${pkgs.gnupg}/bin/gpgconf --list-dirs agent-ssh-socket)
# '';
};
nix = {
package = pkgs.nixFlakes;
extraOptions = ''
experimental-features = nix-command flakes
'';
};
nixpkgs.config.allowUnfree = true;
security.acme.acceptTerms = true;
hardware.enableRedistributableFirmware = true;
krb5 = {
enable = true;
appdefaults = {
forwardable = true;
proxiable = true;
encrypt = true;
forward = true;
};
libdefaults = {
allow_weak_crypto = true;
dns_lookup_kdc = true;
dns_lookup_realm = true;
forwardable = true;
proxiable = true;
};
kerberos = pkgs.heimdalFull;
};
services = {
openssh = {
enable = true;
startWhenNeeded = true;
useDns = true;
permitRootLogin = "prohibit-password";
extraConfig = ''
GSSAPIAuthentication yes
GSSAPICleanupCredentials yes
GSSAPIKeyExchange yes
GSSAPIStoreCredentialsOnRekey yes
'';
};
fail2ban =
let domain-name = config.fudo.hosts.${config.instance.hostname}.domain;
in {
enable = config.networking.firewall.enable;
bantime-increment.enable = true;
ignoreIP = config.fudo.domains.${domain-name}.local-networks;
};
xserver = {
layout = "us";
xkbVariant = "dvp";
xkbOptions = "ctrl:nocaps";
};
# pcscd.enable = true;
# udev.packages = with pkgs; [ yubikey-personalization ];
};
networking.firewall = {
# Allow mosh connections if the firewall is enabled
allowedUDPPortRanges = [{
from = 60000;
to = 60100;
}];
};
console.useXkbConfig = true;
i18n.defaultLocale = "en_US.UTF-8";
programs = {
mosh.enable = true;
bash.enableCompletion = true;
fish.enable = true;
gnupg.agent = {
enable = true;
# enableSSHSupport = true;
# pinentryFlavor = if cfg.enable-gui then "gnome3" else "curses";
};
ssh = {
startAgent = true;
package = pkgs.openssh_gssapi;
extraConfig = ''
GSSAPIAuthentication yes
GSSAPIDelegateCredentials yes
'';
};
};
security.pam = {
enableSSHAgentAuth = true;
services = {
sshd = {
makeHomeDir = true;
sshAgentAuth = true;
# This isn't supposed to ask for a code unless ~/.google_authenticator exists...but it does
# googleAuthenticator.enable = true;
};
};
};
} }

View File

@ -1,7 +1,4 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
with lib; { with lib; {
imports = [ ./common-ui.nix ];
config = { networking = { networkmanager.enable = mkForce false; }; };
} }

View File

@ -2,31 +2,4 @@
with lib; with lib;
{ {
imports = [ ./common-ui.nix ];
options.fudo.profile.laptop = {
use-network-manager =
mkEnableOption "Use NetworkManager instead of wpa_supplicant.";
};
config = {
environment.systemPackages = with pkgs; [ acpi upower wpa_supplicant ];
networking = if (config.fudo.profile.laptop.use-network-manager) then {
networkmanager.enable = true;
} else {
networkmanager.enable = false;
wireless = {
enable = true;
userControlled = {
enable = true;
group = "wheel";
};
networks = mapAttrs (network: networkOpts: {
psk = networkOpts.key;
}) config.fudo.wireless-networks;
};
};
};
} }

View File

@ -1,75 +1,5 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
with lib; with lib;
let {
serverPackages = with pkgs; [ emacs-nox reboot-if-necessary test-config ];
reboot-if-necessary = pkgs.writeShellScriptBin "reboot-if-necessary" ''
if [ $# -ne 1 ]; then
echo "FAILED: no sync file provided."
exit 1
fi
WALL=${pkgs.utillinux}/bin/wall
if [ -f $1 ]; then
$WALL "$1 exists, rebooting system"
${pkgs.systemd}/bin/reboot
else
$WALL "$1 does not exist, switching config."
nixos-rebuild switch
fi
exit 0
'';
test-config = pkgs.writeShellScriptBin "fudo-test-config" ''
if [ $# -gt 1 ]; then
echo "usage: $0 [timeout]"
exit 1
elif [ $# -eq 1 ]; then
TIMEOUT=$1
else
TIMEOUT=15m
fi
SYNCFILE=$TMP/sync-$(date +"%Y%m%d-%H%M%N")
touch $SYNCFILE
${pkgs.utillinux}/bin/wall "Launching config. System will restart in $TIMEOUT if $SYNCFILE still exists."
systemd-run --on-active=$TIMEOUT ${reboot-if-necessary} $SYNCFILE
nixos-rebuild test
exit 0
'';
in {
imports = [ ./common.nix ];
config = {
environment = { systemPackages = serverPackages; };
system.autoUpgrade.enable = false;
networking.networkmanager.enable = mkForce false;
services = { xserver.enable = false; };
sound.enable = false;
hardware.pulseaudio.enable = false;
powerManagement =
if config.fudo.hosts.${config.instance.hostname}.keep-cool then {
enable = true;
cpuFreqGovernor = "ondemand";
} else {
enable = false;
};
systemd.targets = {
sleep.enable = false;
suspend.enable = false;
hibernate.enable = false;
hybrid-sleep.enable = false;
};
};
} }

View File

@ -0,0 +1,131 @@
{ config, lib, pkgs, ... }:
with lib;
let
hostname = config.instance.hostname;
enable-gui = config.fudo.hosts.${hostname}.enable-gui;
in {
imports = [ ./common.nix ];
boot = {
plymouth.enable = false;
tmpOnTmpfs = true;
};
services = {
xserver = mkIf enable-gui {
enable = true;
desktopManager.gnome.enable = true;
displayManager.gdm = {
enable = true;
wayland = false;
autoSuspend = false;
};
windowManager.stumpwm.enable = true;
# windowManager.session = pkgs.lib.singleton {
# name = "stumpwm";
# start = ''
# ${pkgs.lispPackages.stumpwm}/bin/stumpwm &
# waidPID=$!
# '';
# };
};
trezord.enable = true;
};
hardware = {
bluetooth.enable = true;
opengl = mkIf enable-gui {
enable = true;
driSupport = true;
driSupport32Bit = true;
};
};
sound.enable = true;
hardware.pulseaudio = {
enable = true;
support32Bit = config.hardware.pulseaudio.enable;
};
# console.font =
# lib.mkDefault "${pkgs.terminus_font}/share/consolefonts/ter-g18n.psf.gz";
services.gnome = mkIf enable-gui {
evolution-data-server.enable = mkForce false;
gnome-user-share.enable = mkForce false;
};
services.flatpak.enable = enable-gui;
fonts = mkIf enable-gui {
fontDir.enable = true;
fontconfig.enable = true;
#fontconfig.antialias = true;
#fontconfig.penultimate.enable = true;
#fontconfig.subpixel.lcdfilter = "default";
fonts = with pkgs; [
cantarell_fonts
dejavu_fonts
dina-font
dosemu_fonts
fira-code
fira-code-symbols
freefont_ttf
liberation_ttf
mplus-outline-fonts
nerdfonts
noto-fonts
noto-fonts-cjk
noto-fonts-emoji
proggyfonts
terminus_font
ubuntu_font_family
ucsFonts
ultimate-oldschool-pc-font-pack
unifont
xorg.fontadobe100dpi
xorg.fontadobe75dpi
xorg.fontadobeutopia100dpi
xorg.fontadobeutopia75dpi
xorg.fontadobeutopiatype1
xorg.fontarabicmisc
xorg.fontbh100dpi
xorg.fontbh75dpi
xorg.fontbhlucidatypewriter100dpi
xorg.fontbhlucidatypewriter75dpi
xorg.fontbhttf
xorg.fontbhtype1
xorg.fontbitstream100dpi
xorg.fontbitstream75dpi
xorg.fontbitstreamtype1
xorg.fontcronyxcyrillic
xorg.fontcursormisc
xorg.fontdaewoomisc
xorg.fontdecmisc
xorg.fontibmtype1
xorg.fontisasmisc
xorg.fontjismisc
xorg.fontmicromisc
xorg.fontmisccyrillic
xorg.fontmiscethiopic
xorg.fontmiscmeltho
xorg.fontmiscmisc
xorg.fontmuttmisc
xorg.fontschumachermisc
xorg.fontscreencyrillic
xorg.fontsonymisc
xorg.fontsunmisc
xorg.fontwinitzkicyrillic
xorg.fontxfree86type1
];
};
}

144
config/profiles/common.nix Normal file
View File

@ -0,0 +1,144 @@
{ config, lib, pkgs, ... }:
with lib;
let
# Available to all users on the system. Keep it minimal.
global-packages = with pkgs; [
bind
cryptsetup
git
heimdal
openssh_gssapi
tldr
vim
wget
];
in {
environment = {
etc.nixos-live.source = ../../.;
systemPackages = global-packages;
# shellInit = ''
# ${pkgs.gnupg}/bin/gpg-connect-agent /bye
# export SSH_AUTH_SOCK=$(${pkgs.gnupg}/bin/gpgconf --list-dirs agent-ssh-socket)
# '';
};
system.autoUpgrade.enable = false;
nix = {
package = pkgs.nixFlakes;
extraOptions = ''
experimental-features = nix-command flakes
'';
};
nixpkgs.config.allowUnfree = true;
security.acme.acceptTerms = true;
hardware.enableRedistributableFirmware = true;
krb5 = {
enable = true;
appdefaults = {
forwardable = true;
proxiable = true;
encrypt = true;
forward = true;
};
libdefaults = {
allow_weak_crypto = true;
dns_lookup_kdc = true;
dns_lookup_realm = true;
forwardable = true;
proxiable = true;
};
kerberos = pkgs.heimdalFull;
};
services = {
openssh = {
enable = true;
startWhenNeeded = true;
useDns = true;
permitRootLogin = "prohibit-password";
extraConfig = ''
GSSAPIAuthentication yes
GSSAPICleanupCredentials yes
GSSAPIKeyExchange yes
GSSAPIStoreCredentialsOnRekey yes
'';
};
fail2ban = let
domain-name = config.fudo.hosts.${config.instance.hostname}.domain;
in {
enable = config.networking.firewall.enable;
bantime-increment.enable = true;
ignoreIP = config.instance.local-networks;
};
xserver = {
layout = "us";
xkbVariant = "dvp";
xkbOptions = "ctrl:nocaps";
};
# pcscd.enable = true;
# udev.packages = with pkgs; [ yubikey-personalization ];
};
networking.firewall = {
# Allow mosh connections if the firewall is enabled
allowedUDPPortRanges = [{
from = 60000;
to = 60100;
}];
};
console.useXkbConfig = true;
i18n.defaultLocale = "en_US.UTF-8";
programs = {
mosh.enable = true;
bash.enableCompletion = true;
fish.enable = true;
gnupg.agent = {
enable = true;
# enableSSHSupport = true;
# pinentryFlavor = if cfg.enable-gui then "gnome3" else "curses";
};
ssh = {
startAgent = true;
package = pkgs.openssh_gssapi;
extraConfig = ''
GSSAPIAuthentication yes
GSSAPIDelegateCredentials yes
'';
};
};
security.pam = {
enableSSHAgentAuth = true;
services = {
sshd = {
makeHomeDir = true;
sshAgentAuth = true;
# This isn't supposed to ask for a code unless ~/.google_authenticator exists...but it does
# googleAuthenticator.enable = true;
};
};
};
}

View File

@ -0,0 +1,10 @@
{ config, lib, pkgs, ... }:
with lib;
{
imports = [ ./common-ui.nix ];
config = {
networking.networkmanager.enable = mkForce false;
};
}

View File

@ -0,0 +1,32 @@
{ config, lib, pkgs, ... }:
with lib;
{
imports = [ ./common-ui.nix ];
options.fudo.profile.laptop = {
use-network-manager =
mkEnableOption "Use NetworkManager instead of wpa_supplicant.";
};
config = {
environment.systemPackages = with pkgs; [ acpi upower wpa_supplicant ];
networking = if (config.fudo.profile.laptop.use-network-manager) then {
networkmanager.enable = true;
} else {
networkmanager.enable = false;
wireless = {
enable = true;
userControlled = {
enable = true;
group = "wheel";
};
networks = mapAttrs (network: networkOpts: {
psk = networkOpts.key;
}) config.fudo.wireless-networks;
};
};
};
}

View File

@ -0,0 +1,74 @@
{ config, lib, pkgs, ... }:
with lib;
let
reboot-if-necessary = pkgs.writeShellScriptBin "reboot-if-necessary" ''
if [ $# -ne 1 ]; then
echo "FAILED: no sync file provided."
exit 1
fi
WALL=${pkgs.utillinux}/bin/wall
if [ -f $1 ]; then
$WALL "$1 exists, rebooting system"
${pkgs.systemd}/bin/reboot
else
$WALL "$1 does not exist, switching config."
nixos-rebuild switch
fi
exit 0
'';
test-config = pkgs.writeShellScriptBin "fudo-test-config" ''
if [ $# -gt 1 ]; then
echo "usage: $0 [timeout]"
exit 1
elif [ $# -eq 1 ]; then
TIMEOUT=$1
else
TIMEOUT=15m
fi
SYNCFILE=$TMP/sync-$(date +"%Y%m%d-%H%M%N")
touch $SYNCFILE
${pkgs.utillinux}/bin/wall "Launching config. System will restart in $TIMEOUT if $SYNCFILE still exists."
systemd-run --on-active=$TIMEOUT ${reboot-if-necessary} $SYNCFILE
nixos-rebuild test
exit 0
'';
in {
imports = [ ./common.nix ];
config = {
environment = {
serverPackages = with pkgs;
[ emacs-nox reboot-if-necessary test-config ];
};
networking.networkmanager.enable = mkForce false;
services.xserver.enable = false;
sound.enable = false;
hardware.pulseaudio.enable = false;
powerManagement =
if config.fudo.hosts.${config.instance.hostname}.keep-cool then {
enable = true;
cpuFreqGovernor = "ondemand";
} else {
enable = false;
};
systemd.targets = {
sleep.enable = false;
suspend.enable = false;
hibernate.enable = false;
hybrid-sleep.enable = false;
};
};
}

View File

@ -38,29 +38,31 @@ in {
options = [ "comment=systemd.automount" ]; options = [ "comment=systemd.automount" ];
}; };
# NOTE: these are pointing directly to nostromo so the krb lookup works
"/net/documents" = { "/net/documents" = {
device = "sea-store.sea.fudo.org:/export/documents"; device = "nostromo.sea.fudo.org:/export/documents";
fsType = "nfs4"; fsType = "nfs4";
options = [ "comment=systemd.automount" "sec=krb5p" ]; options = [ "comment=systemd.automount" "sec=krb5p" "proto=tcp" ];
}; };
"/net/downloads" = { "/net/downloads" = {
device = "sea-store.sea.fudo.org:/export/downloads"; device = "nostromo.sea.fudo.org:/export/downloads";
fsType = "nfs4"; fsType = "nfs4";
options = [ "comment=systemd.automount" "sec=krb5i" ]; options = [ "comment=systemd.automount" "sec=krb5i" "proto=tcp" ];
}; };
"/net/projects" = { "/net/projects" = {
device = "sea-store.sea.fudo.org:/export/projects"; device = "nostromo.sea.fudo.org:/export/projects";
fsType = "nfs4"; fsType = "nfs4";
options = [ "comment=systemd.automount" "sec=krb5p" ]; options = [ "comment=systemd.automount" "sec=krb5p" "proto=tcp" ];
}; };
}; };
systemd = { systemd = {
tmpfiles.rules = [ ## This fails if the filesystems already exist
"d /net/documents - root sea-documents - -" # tmpfiles.rules = [
"d /net/downloads - root sea-downloads - -" # "d /net/documents - root sea-documents - -"
"d /net/projects - root sea-projects - -" # "d /net/downloads - root sea-downloads - -"
]; # "d /net/projects - root sea-projects - -"
# ];
# mounts = [ # mounts = [
# { # {

17
config/system-users.nix Normal file
View File

@ -0,0 +1,17 @@
{
replicator = {
description = "Database Replicator";
hashed-password = "{SHA}HpiRMyxLR+0ZFHz/COvG9lcNYyQ=";
};
auth_reader = {
description = "System Authenticator";
hashed-password = "{MD5}N36/kQ64mev1HARddvVk7Q==";
};
user_db_reader = {
description = "User Database Reader";
hashed-password = "{SSHA}IVKhrB+wMOCI/CCzbJW8sNDbH67ZTMBv";
};
}

View File

@ -1,4 +1,4 @@
{ hostname, site, domain, profile, build-timestamp, ... }: { lib, pkgs, hostname, site, domain, profile, build-timestamp, ... }:
let let
# Get info on this host so we know what to load # Get info on this host so we know what to load

View File

@ -1,6 +1,6 @@
{ lib, ... }: { pkgs, ... }:
with lib; with pkgs.lib;
let let
join-lines = concatStringsSep "\n"; join-lines = concatStringsSep "\n";

View File

@ -1,16 +1,18 @@
{ lib, ... }: { lib, ... }:
let # NOTE: OBSOLETE! See overlay.nix
ip = import ./ip.nix { inherit lib; };
dns = import ./dns.nix { inherit lib; };
passwd = import ./passwd.nix { inherit lib; };
in
{ {
lib.overlays = [ lib.overlays = [
(final: prev: (final: prev:
prev.lib // { prev.lib // {
fudo = { fudo = let
inherit ip dns passwd; lib = prev.lib;
in {
ip = import ./ip.nix { inherit lib; };
dns = import ./dns.nix { inherit lib; };
passwd = import ./passwd.nix { inherit lib; };
lisp = import ./lisp.nix { inherit lib; };
}; };
}) })
]; ];

View File

@ -30,7 +30,9 @@ let
# }; # };
# }; # };
domainOpts = { domain, ... }: { domainOpts = { name, ... }: let
domain = name;
in {
options = with types; { options = with types; {
email = mkOption { email = mkOption {
type = str; type = str;
@ -45,8 +47,12 @@ let
}; };
local-copies = let local-copies = let
localCopyOpts = { copy, ... }: { localCopyOpts = { name, ... }: let
options = with types; { copy = name;
in {
options = with types; let
target-path = "/var/run/${domain}/${copy}";
in {
user = mkOption { user = mkOption {
type = str; type = str;
description = "User to which this copy belongs."; description = "User to which this copy belongs.";
@ -58,17 +64,35 @@ let
default = null; default = null;
}; };
path = mkOption {
type = str;
description = "Path at which to store the local copy.";
default = "/var/run/${domain}/${copy}";
};
service = mkOption { service = mkOption {
type = str; type = str;
description = "systemd job to copy certs."; description = "systemd job to copy certs.";
default = "fudo-${domain}-${copy}-certs.service"; default = "fudo-${domain}-${copy}-certs.service";
}; };
certificate = mkOption {
type = str;
description = "Full path to the local copy certificate.";
default = "${target-path}/cert.pem";
};
full-certificate = mkOption {
type = str;
description = "Full path to the local copy certificate.";
default = "${target-path}/fullchain.pem";
};
chain = mkOption {
type = str;
description = "Full path to the local copy certificate.";
default = "${target-path}/chain.pem";
};
private-key = mkOption {
type = str;
description = "Full path to the local copy certificate.";
default = "${target-path}/key.pem";
};
}; };
}; };
in mkOption { in mkOption {
@ -92,7 +116,7 @@ let
cfg.host-domains.${hostname} else {}; cfg.host-domains.${hostname} else {};
optionalStringOr = str: default: optionalStringOr = str: default:
if cond then str else default; if (str != null) then str else default;
in { in {
options.fudo.acme = with types; { options.fudo.acme = with types; {
@ -113,26 +137,41 @@ in {
tmpfiles.rules = let tmpfiles.rules = let
copies = concatMapAttrs (domain: domainOpts: copies = concatMapAttrs (domain: domainOpts:
domainOpts.local-copies) localDomains; domainOpts.local-copies) localDomains;
perms = copyOpts: if (copyOpts.group != null) then "0550" else "0500";
copy-paths = mapAttrsToList (copy: copyOpts: copy-paths = mapAttrsToList (copy: copyOpts:
"D '${path}' 0550 ${copyOpts.user} ${optionalStringOr copyOpts.group "-"} - -") let
copies; dir-entry = copyOpts: file: "D '${dirOf file}' ${perms copyOpts} ${copyOpts.user} ${optionalStringOr copyOpts.group "-"} - -";
in copy-paths; in map (dir-entry copyOpts) [
copyOpts.certificate
copyOpts.full-certificate
copyOpts.chain
copyOpts.private-key
]) copies;
in unique copy-paths;
# TODO: Make this a Fudo service?
services = concatMapAttrs (domain: domainOpts: services = concatMapAttrs (domain: domainOpts:
mapAttrs' (copy: copyOpts: let mapAttrs' (copy: copyOpts: let
key-perms = copyOpts: if (copyOpts.group != null) then "0440" else "0400";
source = config.security.acme.certs.${domain}.directory; source = config.security.acme.certs.${domain}.directory;
target = copyOpts.path; target = copyOpts.path;
install-certs = pkgs.writeShellScript "fudo-install-${domain}-${site}-certs.sh" '' install-certs = pkgs.writeShellScript "fudo-install-${domain}-${copy}-certs.sh" ''
for cert in cert chain fullchain full key; do cp cert.pem ${copyOpts.certificate}
cp ${source}/$cert.pem ${target}/$cert.pem chmod 0444 ${copyOpts.certificate}
chmod 0440 ${source}/$cert.pem
done cp full.pem ${copyOpts.full-certificate}
chmod 0444 ${copyOpts.full-certificate}
cp chain.pem ${copyOpts.chain}
chmod 0444 ${copyOpts.chain}
cp key.pem ${copyOpts.private-key}
chmod ${key-perms copyOpts} ${copyOpts.private-key}
''; '';
remove-certs = pkgs.writeShellScript "fudo-remove-${domain}-${site}-certs.sh" '' remove-certs = pkgs.writeShellScript "fudo-remove-${domain}-${copy}-certs.sh" ''
for cert in cert chain fullchain full key; do rm -f ${copyOpts.private-key}
rm -rf ${target}/$cert.pem rm -f ${copyOpts.chainy}
done rm -f ${copyOpts.full-certificate}
rm -f ${copyOpts.certificate}
''; '';
in nameValuePair in nameValuePair
(rm-service-ext copyOpts.service) { (rm-service-ext copyOpts.service) {

View File

@ -1,5 +1,6 @@
{ ... }: { config, pkgs, lib, ... }:
with lib;
{ {
imports = [ imports = [
./dns.nix ./dns.nix

View File

@ -1,63 +1,10 @@
{ config, pkgs, lib, ... }: { config, pkgs, lib, ... }:
with lib; with lib;
let let
cfg = config.fudo.backplane.dns; cfg = config.fudo.backplane.dns;
lisp-pkgs = with pkgs.localLispPackages; [ powerdns-conf-dir = "${cfg.powerdns-home}/conf.d";
arrows
backplane-dns
backplane-server
cl-sasl
cl-xmpp
ip-utils
alexandria
babel
bordeaux-threads
cffi
cl-base64
cl-json
cl-postgres
cl-ppcre
cl-unicode
cl_plus_ssl
closer-mop
closure-common
cxml
flexi-streams
global-vars
introspect-environment
ironclad
iterate
lisp-namespace
md5
nibbles
postmodern
puri
s-sql
split-sequence
trivia
trivia_dot_balland2006
trivia_dot_level0
trivia_dot_level1
trivia_dot_level2
trivia_dot_trivial
trivial-cltl2
trivial-features
trivial-garbage
trivial-gray-streams
type-i
uax-15
usocket
];
backup-directory = "/var/lib/fudo/backplane/dns";
powerdns-home = "/var/lib/powerdns";
powerdns-conf-dir = "${powerdns-home}/conf.d";
backplaneOpts = { ... }: { backplaneOpts = { ... }: {
options = { options = {
@ -117,53 +64,59 @@ let
}; };
in { in {
options.fudo.backplane.dns = { options.fudo.backplane.dns = with types; {
enable = mkEnableOption "Enable backplane dynamic DNS server."; enable = mkEnableOption "Enable backplane dynamic DNS server.";
port = mkOption { port = mkOption {
type = types.port; type = port;
description = "Port on which to serve authoritative DNS requests."; description = "Port on which to serve authoritative DNS requests.";
default = 53; default = 53;
}; };
listen-v4-addresses = mkOption { listen-v4-addresses = mkOption {
type = with types; listOf str; type = listOf str;
description = "IPv4 addresses on which to listen for dns requests."; description = "IPv4 addresses on which to listen for dns requests.";
default = [ "0.0.0.0" ]; default = [ "0.0.0.0" ];
}; };
listen-v6-addresses = mkOption { listen-v6-addresses = mkOption {
type = with types; listOf str; type = listOf str;
description = "IPv6 addresses on which to listen for dns requests."; description = "IPv6 addresses on which to listen for dns requests.";
example = [ "[abcd::1]" ]; example = [ "[abcd::1]" ];
default = [ ]; default = [ ];
}; };
required-services = mkOption { required-services = mkOption {
type = with types; listOf str; type = listOf str;
description = description =
"A list of services required before the DNS server can start."; "A list of services required before the DNS server can start.";
}; };
user = mkOption { user = mkOption {
type = types.str; type = str;
description = "User as which to run DNS backplane listener service."; description = "User as which to run DNS backplane listener service.";
default = "backplane-dns"; default = "backplane-dns";
}; };
group = mkOption { group = mkOption {
type = types.str; type = str;
description = "Group as which to run DNS backplane listener service."; description = "Group as which to run DNS backplane listener service.";
default = "backplane-dns"; default = "backplane-dns";
}; };
database = mkOption { database = mkOption {
type = with types; submodule databaseOpts; type = submodule databaseOpts;
description = "Database settings for the DNS server."; description = "Database settings for the DNS server.";
}; };
powerdns-home = mkOption {
type = str;
description = "Directory at which to store powerdns configuration and state.";
default = "/run/backplane-dns/powerdns";
};
backplane = mkOption { backplane = mkOption {
type = with types; submodule backplaneOpts; type = submodule backplaneOpts;
description = "Backplane Jabber settings for the DNS server."; description = "Backplane Jabber settings for the DNS server.";
}; };
}; };
@ -177,7 +130,11 @@ in {
createHome = true; createHome = true;
home = "/var/home/${cfg.user}"; home = "/var/home/${cfg.user}";
}; };
backplane-powerdns = { isSystemUser = true; }; backplane-powerdns = {
isSystemUser = true;
home = cfg.powerdns-home;
createHome = true;
};
}; };
groups = { groups = {
@ -186,65 +143,17 @@ in {
}; };
}; };
systemd = { fudo.system.services = {
targets = { backplane-powerdns-config-generator = {
backplane-dns = {
description = "Fudo DNS backplane services.";
wantedBy = [ "multi-user.target" ];
};
};
services = {
backplane-powerdns = let
configDir = pkgs.writeTextDir "pdns.conf" ''
local-address=${lib.concatStringsSep ", " cfg.listen-v4-addresses}
local-ipv6=${lib.concatStringsSep ", " cfg.listen-v6-addresses}
local-port=${toString cfg.port}
launch=
include-dir=${powerdns-conf-dir}/
'';
psql-user = config.services.postgresql.superUser;
in {
unitConfig.Documentation = "man:pdns_server(1) man:pdns_control(1)";
description = "Backplane PowerDNS name server";
requires = [
"postgresql.service"
"backplane-dns-config-generator.service"
"backplane-dns.target"
];
after = [ "network.target" "postgresql.service" ];
wantedBy = [ "multi-user.target" ];
path = with pkgs; [ postgresql ];
serviceConfig = {
Restart = "on-failure";
RestartSec = "10";
StartLimitInterval = "0";
PrivateDevices = true;
# CapabilityBoundingSet="CAP_CHOWN CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_SYS_CHROOT";
# NoNewPrivileges=true;
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p ${powerdns-home}";
ExecStart =
"${pkgs.powerdns}/bin/pdns_server --setuid=backplane-powerdns --setgid=backplane-powerdns --chroot=${powerdns-home} --socket-dir=/ --daemon=no --guardian=no --disable-syslog --write-pid=no --config-dir=${configDir}";
ProtectSystem = "full";
# ProtectHome=true;
RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
};
};
backplane-dns-config-generator = {
description = description =
"Generate postgres configuration for backplane DNS server."; "Generate postgres configuration for backplane DNS server.";
requiredBy = [ "backplane-powerdns.service" ];
requires = cfg.required-services; requires = cfg.required-services;
serviceConfig.Type = "oneshot"; type = "oneshot";
restartIfChanged = true; restartIfChanged = true;
partOf = [ "backplane-dns.target" ]; partOf = [ "backplane-dns.target" ];
readWritePaths = [ powerdns-conf-dir ];
preStart = '' preStart = ''
mkdir -p ${powerdns-conf-dir} mkdir -p ${powerdns-conf-dir}
chown backplane-powerdns:backplane-powerdns ${powerdns-conf-dir} chown backplane-powerdns:backplane-powerdns ${powerdns-conf-dir}
@ -277,30 +186,42 @@ in {
echo "gpgsql-dnssec=yes" >> $TMPCONF echo "gpgsql-dnssec=yes" >> $TMPCONF
mv $TMPCONF ${powerdns-conf-dir}/pdns.local.gpgsql.conf mv $TMPCONF ${powerdns-conf-dir}/pdns.local.gpgsql.conf
rm -rf $TMPDIR rm -rf $TMPDIR
exit 0 exit 0
''; '';
}; };
backplane-powerdns = let
pdns-config-dir = pkgs.writeTextDir "pdns.conf" ''
local-address=${lib.concatStringsSep ", " cfg.listen-v4-addresses}
local-ipv6=${lib.concatStringsSep ", " cfg.listen-v6-addresses}
local-port=${toString cfg.port}
launch=
include-dir=${powerdns-conf-dir}/
'';
in {
description = "Backplane PowerDNS name server";
requires = [
"postgresql.service"
"backplane-powerdns-config-generator.service"
];
after = [ "network.target" ];
path = with pkgs; [ powerdns postgresql ];
execStart = "pdns_server --setuid=backplane-powerdns --setgid=backplane-powerdns --chroot=${cfg.powerdns-home} --socket-dir=/ --daemon=no --guardian=no --disable-syslog --write-pid=no --config-dir=${pdns-config-dir}";
};
backplane-dns = { backplane-dns = {
description = "Fudo DNS Backplane Server"; description = "Fudo DNS Backplane Server";
restartIfChanged = true; restartIfChanged = true;
path = with pkgs; [ backplane-dns-server ];
serviceConfig = { execStart = "launch-backplane-dns.sh";
ExecStart = pidFile = "/run/backplane-dns.$USERNAME.pid";
"${pkgs.backplane-dns-server}/bin/launch-backplane-dns.sh"; user = cfg.user;
Restart = "on-failure"; group = cfg.group;
PIDFile = "/run/backplane-dns.$USERNAME.pid"; partOf = [ "backplane-dns.target" ];
User = cfg.user; requires = [ "postgresql.service" ];
Group = cfg.group;
StandardOutput = "journal";
};
environment = { environment = {
# LD_LIBRARY_PATH = "${pkgs.openssl_1_1.out}/lib";
FUDO_DNS_BACKPLANE_XMPP_HOSTNAME = cfg.backplane.host; FUDO_DNS_BACKPLANE_XMPP_HOSTNAME = cfg.backplane.host;
FUDO_DNS_BACKPLANE_XMPP_USERNAME = cfg.backplane.role; FUDO_DNS_BACKPLANE_XMPP_USERNAME = cfg.backplane.role;
FUDO_DNS_BACKPLANE_XMPP_PASSWORD_FILE = cfg.backplane.password-file; FUDO_DNS_BACKPLANE_XMPP_PASSWORD_FILE = cfg.backplane.password-file;
@ -311,15 +232,18 @@ in {
FUDO_DNS_BACKPLANE_DATABASE_PASSWORD_FILE = FUDO_DNS_BACKPLANE_DATABASE_PASSWORD_FILE =
cfg.backplane.database.password-file; cfg.backplane.database.password-file;
# CL_SOURCE_REGISTRY = "${pkgs.localLispPackages.backplane-dns}//";
CL_SOURCE_REGISTRY = CL_SOURCE_REGISTRY =
lib.concatStringsSep ":" (map (pkg: "${pkg}//") lisp-pkgs); pkgs.lib.fudo.lisp.lisp-source-registry pkgs.backplane-dns-server;
};
};
}; };
requires = cfg.required-services; systemd = {
partOf = [ "backplane-dns.target" ]; targets = {
backplane-dns = {
description = "Fudo DNS backplane services.";
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
requries = cfg.required-services ++ [ "postgresql.service" ];
}; };
}; };
}; };

View File

@ -108,7 +108,7 @@ in {
enable = true; enable = true;
appName = cfg.site-name; appName = cfg.site-name;
database = { database = {
createDatabase = true; createDatabase = false;
host = cfg.database.hostname; host = cfg.database.hostname;
name = cfg.database.name; name = cfg.database.name;
user = cfg.database.user; user = cfg.database.user;

View File

@ -34,8 +34,9 @@ in {
systemd = { systemd = {
# Ensure the mountpoints exist # Ensure the mountpoints exist
tmpfiles.rules = let tmpfiles.rules = let
mpPerms = mpOpts: if mpOpts.world-readable then "755" else "750";
mountpointToPath = mp: mpOpts: mountpointToPath = mp: mpOpts:
"d '${mp}' 750 root ${optionalOrDefault mpOpts.group "-"} - -"; "d '${mp}' ${mpPerms mpOpts} root ${optionalOrDefault mpOpts.group "-"} - -";
filesystemsToMountpointLists = mapAttrsToList filesystemsToMountpointLists = mapAttrsToList
(fs: fsOpts: fsOpts.mountpoints); (fs: fsOpts: fsOpts.mountpoints);
mountpointListsToPaths = concatMap mountpointListsToPaths = concatMap

View File

@ -6,6 +6,10 @@ let
host = import ../types/host.nix { inherit lib; }; host = import ../types/host.nix { inherit lib; };
hostname = config.instance.hostname;
host-secrets = config.fudo.secrets.host-secrets.${hostname};
in { in {
options.fudo.hosts = with types; options.fudo.hosts = with types;
mkOption { mkOption {
@ -73,6 +77,8 @@ in {
config.environment.systemPackages; config.environment.systemPackages;
sorted-unique = sort lessThan (unique packages); sorted-unique = sort lessThan (unique packages);
in concatStringsSep "\n" sorted-unique; in concatStringsSep "\n" sorted-unique;
build-timestamp.text = toString config.instance.build-timestamp;
}; };
systemPackages = with pkgs; systemPackages = with pkgs;
@ -103,10 +109,6 @@ in {
(keypair: keypair.private-key) (keypair: keypair.private-key)
(try-attr hostname files.build-keypairs); (try-attr hostname files.build-keypairs);
backplane-passwd-source = try-attr hostname files.backplane-passwords;
backplane-passwd-target = "/var/run/backplane/passwd";
in { in {
secrets.host-secrets.${hostname} = { secrets.host-secrets.${hostname} = {
host-keytab = mkIf (keytab-file != null) { host-keytab = mkIf (keytab-file != null) {
@ -121,15 +123,15 @@ in {
user = "root"; user = "root";
}; };
backplane-passwd = mkIf (backplane-passwd-source != null) { backplane-passwd = {
source-file = backplane-passwd-source; source-file = host-cfg.backplane-password-file;
target-file = backplane-passwd-target; target-file = "/run/backplane/client/passwd";
user = config.fudo.client.dns.user; user = config.fudo.client.dns.user;
}; };
}; };
client.dns.password-file = mkIf (backplane-passwd-source != null) client.dns.password-file =
backplane-passwd-target; host-secrets.backplane-passwd.target-file;
}; };
programs.adb.enable = host-cfg.android-dev; programs.adb.enable = host-cfg.android-dev;

View File

@ -23,16 +23,16 @@ let
foldr (a: b: a // b) {} (mapAttrs f attrs); foldr (a: b: a // b) {} (mapAttrs f attrs);
concatMapAttrsToList = f: attr: concatMapAttrsToList = f: attr:
attrValues (concatMapAttrs f attr); concatMap (i: i) (attrValues (mapAttrs f attr));
host-domains = config.fudo.acme.host-domains.${hostname}; host-domains = config.fudo.acme.host-domains.${hostname};
siteCerts = site: let siteCerts = site: let
certPath = host-domains.${site}.local-copies.ejabberd.path; cert-copy = host-domains.${site}.local-copies.ejabberd;
in [ in [
"${certPath}/fullchain.pem" cert-copy.certificate
"${certPath}/privkey.pem" cert-copy.private-key
"${certPath}/chain.pem" cert-copy.chain
]; ];
siteCertService = site: siteCertService = site:
@ -60,13 +60,13 @@ let
hosts = attrNames cfg.sites; hosts = attrNames cfg.sites;
listen = [{ listen = map (ip: {
port = cfg.port; port = cfg.port;
module = "ejabberd_c2s"; module = "ejabberd_c2s";
ip = cfg.listen-ip; ip = ip;
starttls = true; starttls = true;
starttls_required = true; starttls_required = true;
}]; }) cfg.listen-ips;
certfiles = concatMapAttrsToList certfiles = concatMapAttrsToList
(site: siteOpts: (site: siteOpts:
@ -94,7 +94,6 @@ let
swapper = concatStringsSep " | " secret-swappers; swapper = concatStringsSep " | " secret-swappers;
in pkgs.writeShellScript "ejabberd-generate-config.sh" '' in pkgs.writeShellScript "ejabberd-generate-config.sh" ''
cat ${template} | ${swapper} > ${target} cat ${template} | ${swapper} > ${target}
chown ${cfg.user}:${cfg.group} ${target}
''; '';
cfg = config.fudo.jabber; cfg = config.fudo.jabber;
@ -103,6 +102,11 @@ in {
options.fudo.jabber = with types; { options.fudo.jabber = with types; {
enable = mkEnableOption "Enable ejabberd server."; enable = mkEnableOption "Enable ejabberd server.";
listen-ips = mkOption {
type = listOf str;
description = "IPs on which to listen for Jabber connections.";
};
port = mkOption { port = mkOption {
type = port; type = port;
description = "Port on which to listen for Jabber connections."; description = "Port on which to listen for Jabber connections.";
@ -122,7 +126,7 @@ in {
}; };
admins = mkOption { admins = mkOption {
type = str; type = listOf str;
description = "List of admin users for the server."; description = "List of admin users for the server.";
default = []; default = [];
}; };
@ -141,7 +145,23 @@ in {
config-file = mkOption { config-file = mkOption {
type = str; type = str;
description = "Location at which to generate the configuration file."; description = "Location at which to generate the configuration file.";
default = "/var/run/ejabberd/ejabberd.yaml"; default = "/run/ejabberd/ejabberd.yaml";
};
log-level = mkOption {
type = int;
description = ''
Log level at which to run the server.
See: https://docs.ejabberd.im/admin/guide/troubleshooting/
'';
default = 3;
};
environment = mkOption {
type = attrsOf str;
description = "Environment variables to set for the ejabberd daemon.";
default = {};
}; };
}; };
@ -156,7 +176,8 @@ in {
}; };
}; };
fudo.acme.host-domains.${hostname} = mapAttrs (site: siteCfg: fudo = {
acme.host-domains.${hostname} = mapAttrs (site: siteCfg:
mkIf siteCfg.enableACME { mkIf siteCfg.enableACME {
local-copies.ejabberd = { local-copies.ejabberd = {
user = cfg.user; user = cfg.user;
@ -164,18 +185,41 @@ in {
}; };
}) cfg.sites; }) cfg.sites;
system = let
config-dir = dirOf cfg.config-file;
in {
ensure-directories.${config-dir} = {
user = cfg.user;
perms = "0700";
};
services.ejabberd-config-generator = let
config-generator =
enter-secrets config-file-template cfg.secret-files cfg.config-file;
in {
script = "${config-generator}";
readWritePaths = [ config-dir ];
workingDirectory = config-dir;
user = cfg.user;
description = "Generate ejabberd config file with necessary passwords.";
postStart = ''
chown ${cfg.user} ${cfg.config-file}
chmod 0400 ${cfg.config-file}
'';
};
};
};
systemd = { systemd = {
tmpfiles.rules = [ tmpfiles.rules = [
"D '${dirOf cfg.config-file}' 0550 ${cfg.user} ${cfg.group} - -" "D '${dirOf cfg.config-file}' 0550 ${cfg.user} ${cfg.group} - -"
]; ];
services.ejabberd = let services = {
config-generator = enter-secrets config-file-template cfg.secret-files cfg.config-file; ejabberd = {
in {
wants = map (site: siteCertService site) (attrNames cfg.sites); wants = map (site: siteCertService site) (attrNames cfg.sites);
environment = cfg.secret-files; requires = [ "ejabberd-config-generator.service" ];
serviceConfig = { environment = cfg.environment;
ExecStartPre = mkAfter "${config-generator}";
}; };
}; };
}; };

View File

@ -205,6 +205,7 @@ in {
ssl-ca-certificate = mkOption { ssl-ca-certificate = mkOption {
type = with types; nullOr str; type = with types; nullOr str;
description = '' description = ''
The path to the SSL CA cert used to sign the certificate. The path to the SSL CA cert used to sign the certificate.
''; '';
@ -220,9 +221,8 @@ in {
base = mkOption { base = mkOption {
type = str; type = str;
description = '' description = "The base dn of the LDAP server.";
The base dn of the LDAP server (eg. "dc=fudo,dc=org"). example = "dc=fudo,dc=org";
'';
}; };
rootpw-file = mkOption { rootpw-file = mkOption {
@ -239,7 +239,6 @@ in {
A list of URIs on which the ldap server should listen. A list of URIs on which the ldap server should listen.
''; '';
example = [ "ldap://auth.fudo.org" "ldaps://auth.fudo.org" ]; example = [ "ldap://auth.fudo.org" "ldaps://auth.fudo.org" ];
default = [ ];
}; };
users = mkOption { users = mkOption {
@ -295,12 +294,11 @@ in {
etc = { etc = {
"openldap/sasl2/slapd.conf" = { "openldap/sasl2/slapd.conf" = {
mode = "0400"; mode = "0400";
user = "openldap"; user = config.services.openldap.user;
group = "openldap"; group = config.services.openldap.group;
# FIXME: take arguments!
text = '' text = ''
mech_list: gssapi external mech_list: gssapi external
keytab: /etc/ldap/ldap.keytab keytab: ${cfg.kerberos-keytab}
''; '';
}; };
}; };
@ -343,72 +341,126 @@ in {
rootdn = "cn=admin,${cfg.base}"; rootdn = "cn=admin,${cfg.base}";
rootpwFile = "${cfg.rootpw-file}"; rootpwFile = "${cfg.rootpw-file}";
urlList = cfg.listen-uris; urlList = cfg.listen-uris;
database = "mdb";
extraConfig = '' settings = let
makeAccessLine = i: attrs: perm-map: let
perm-strings = mapAttrs (dn: perm: "by ${dn} ${perm}") perm-map;
perm-string = concatStringsSep " " perm-strings;
in "${i}to ${attrs} ${perm-string}";
TLSCertificateFile ${cfg.ssl-certificate} makeAccess = access-map: let
TLSCertificateKeyFile ${cfg.ssl-private-key} pairs = mapAttrsToList (target: perm-map: [target perm-map]) access-map;
${optionalString (cfg.ssl-ca-certificate != null) in imap0 (i: pair: makeAccessLine i pair[0] pair[1]) pairs;
"TLSCACertificateFile ${cfg.ssl-ca-certificate}"}
authz-regexp "^uid=auth/([^.]+)\.fudo\.org,cn=fudo\.org,cn=gssapi,cn=auth$" "cn=$1,ou=hosts,dc=fudo,dc=org" in {
authz-regexp "^uid=[^,/]+/root,cn=fudo\.org,cn=gssapi,cn=auth$" "cn=admin,dc=fudo,dc=org" attrs = {
authz-regexp "^uid=([^,/]+),cn=fudo\.org,cn=gssapi,cn=auth$" "uid=$1,ou=members,dc=fudo,dc=org" cn = "config";
authz-regexp "^uid=host/([^,/]+),cn=fudo\.org,cn=gssapi,cn=auth$" "cn=$1,ou=hosts,dc=fudo,dc=org" objectClass = "olcGlobal";
authz-regexp "^gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth$" "cn=admin,dc=fudo,dc=org" olcPidFile = "/run/slapd/slapd.pid";
olcTLSCertificateFile = cfg.ssl-certificate;
''; olcTLSCertificateKeyFile = cfg.ssl-private-key;
olcTLSCACertificateFile = cfg.ssl-ca-certificate;
extraDatabaseConfig = '' olcSaslSecProps = "noplain,noanonymous";
# access to dn=base="" olcAuthzRegexp = let
# by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage authz-regex-entry = i: { regex, target}:
# by * read "{${i}}\"${rx}\" \"${target}\"";
in imap0 authz-regex-entry [
access to attrs=userPassword,shadowLastChange {
by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage regex = "^uid=auth/([^.]+).fudo.org,cn=fudo.org,cn=gssapi,cn=auth$";
by group.exact="cn=admin,ou=members,${cfg.base}" write target = "cn=$1,ou=hosts,dc=fudo,dc=org";
by dn.exact="cn=auth_reader,${cfg.base}" read }
by dn.exact="cn=replicator,${cfg.base}" read {
by self write regex = "^uid=[^,/]+/root,cn=fudo.org,cn=gssapi,cn=auth$";
by * auth target = "cn=admin,dc=fudo,dc=org";
}
access to dn.exact="cn=admin,ou=groups,${cfg.base}" {
by dn.exact="cn=admin,${cfg.base}" write regex = "^uid=([^,/]+),cn=fudo.org,cn=gssapi,cn=auth$";
by users read target = "uid=$1,ou=members,dc=fudo,dc=org";
by * none }
{
access to dn.subtree="ou=groups,${cfg.base}" attrs=memberUid regex = "^uid=host/([^,/]+),cn=fudo.org,cn=gssapi,cn=auth$";
by dn.regex="cn=[a-zA-Z][a-zA-Z0-9_]+,ou=hosts,${cfg.base}" write target = "cn=$1,ou=hosts,dc=fudo,dc=org";
by group.exact="cn=admin,ou=groups,${cfg.base}" write }
by users read {
by * none regex = "^gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth$";
target = "cn=admin,dc=fudo,dc=org";
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 children = {
by * none "olcDatabase{-1}frontend" = {
attrs = {
access to dn.exact="cn=admin,ou=groups,${cfg.base}" objectClass = [ "olcDatabaseConfig" "olcFrontendConfig" ];
by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage olcDatabase = "{-1}frontend";
by users read olcAccess = makeAccess {
by * none "*" = {
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
access to dn.subtree="ou=groups,${cfg.base}" attrs=memberUid "dn.exact=cn=admin,dc=fudo,dc=org" = "manage";
by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage "*" = "none";
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 };
"olcDatabase{0}config" = {
access to * attrs = {
by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage objectClass = [ "olcDatabaseConfig" ];
by users read olcDatabase = "{0}config";
by * none olcAccess = [ "by * none" ];
};
};
index objectClass,uid eq "olcDatabase{1}mdb" = {
''; attrs = {
objectClass = [ "olcDatabaseConfig" "olcMdbConfig" ];
olcDatabase = "{1}mdb";
olcSuffix = cfg.base;
# olcRootDN = "cn=admin,${cfg.base}";
# olcRootPW = FIXME; # NOTE: this should be hashed...
olcDbDirectory = cfg.database-directory;
olcDbIndex = [ "objectClass eq" "uid eq" ];
olcAccess = makeAccess {
"attrs=userPassword,shadowLastChange" = {
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
"group/groupOfNames/member.exact=cn=admin,ou=groups,${cfg.base}" = "write";
"dn.exact=cn=auth_reader,${cfg.base}" = "read";
"dn.exact=cn=replicator,${cfg.base}" = "read";
"self" = "write";
"*" = "auth";
};
"dn.base=cn=admin,ou=groups,${cfg.base}" = {
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
"dn.exact=cn=admin,ou=groups,${cfg.base}" = "write";
"users" = "read";
"*" = "none";
};
"dn.subtree=ou=groups,${cfg.base} attrs=memberUid" = {
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
"dn.exact=cn=admin,ou=groups,${cfg.base}" = "write";
"dn.regex=cn=[a-zA-Z][a-zA-Z0-9_]+,ou=hosts,${cfg.base}" = "write";
"group/groupOfNames/member.exact=cn=admin,ou=groups,${cfg.base}" = "write";
"users" = "read";
"*" = "none";
};
"dn.subtree=ou=members,${cfg.base} attrs=cn,sn,homeDirectory,loginShell,gecos,description,homeDirectory,uidNumber,gidNumber" = {
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
"dn.exact=cn=admin,ou=groups,${cfg.base}" = "write";
"group/groupOfNames/member.exact=cn=admin,ou=groups,${cfg.base}" = "write";
"dn.exact=cn=user_db_reader,${cfg.base}" = "read";
"users" = "read";
"*" = "none";
};
"*" = {
"dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" = "manage";
"dn.exact=cn=admin,ou=groups,${cfg.base}" = "write";
"group/groupOfNames/member.exact=cn=admin,ou=groups,${cfg.base}" = "read";
"users" = "read";
"*" = "none";
};
};
};
};
};
};
declarativeContents = '' declarativeContents = ''
dn: ${cfg.base} dn: ${cfg.base}

View File

@ -1,66 +1,31 @@
{ lib, config, ... }: { lib, config, ... }:
with lib; with lib;
let let
hostname = config.instance.hostname;
cfg = config.fudo.mail-server; cfg = config.fudo.mail-server;
container-maildir = "/var/lib/mail"; container-maildir = "/var/lib/mail";
container-statedir = "/var/lib/mail-state"; container-statedir = "/var/lib/mail-state";
container-shared = "container/mail-server";
container-postfix-cert = "${container-shared}/postfix/cert.pem";
container-postfix-key = "${container-shared}/postfix/key.pem";
container-dovecot-cert = "${container-shared}/dovecot/cert.pem";
container-dovecot-key = "${container-shared}/dovecot/key.pem";
container-fudo-ca-cert = "${container-shared}/fudo-ca.pem";
# Don't bother with group-id, nixos doesn't seem to use it anyway # Don't bother with group-id, nixos doesn't seem to use it anyway
container-mail-user = "mailer"; container-mail-user = "mailer";
container-mail-user-id = 542; container-mail-user-id = 542;
container-mail-group = "mailer"; container-mail-group = "mailer";
fudo-cfg = config.fudo.common;
in rec { in rec {
options.fudo.mail-server.container = { config = mkIf (cfg.enableContainer) {
ldap-url = mkOption { # Disable postfix on this host--it'll be run in the container instead
type = types.str;
description = "URL of the LDAP server to use for authentication.";
example = "ldaps://auth.fudo.org/";
};
};
config = mkIf (cfg.enableContainer && !cfg.enable) {
# Disable postfix on thi host--it'll be run in the container instead
services.postfix.enable = false; services.postfix.enable = false;
# Copy data intended for the container to a path in /etc which can be fudo.acme.host-domains.${hostname}.${cfg.mail-hostname} = {
# bind-mounted. local-copies = {
environment.etc = { postfix = {
"${container-postfix-cert}" = { user = "root";
mode = "0444"; };
source = cfg.postfix.ssl-certificate; dovecot-cert = {
user = "root";
}; };
"${container-postfix-key}" = {
mode = "0400";
source = cfg.postfix.ssl-private-key;
};
"${container-dovecot-cert}" = {
mode = "0444";
source = cfg.dovecot.ssl-certificate;
};
"${container-dovecot-key}" = {
mode = "0400";
source = cfg.dovecot.ssl-private-key;
};
"${container-fudo-ca-cert}" = {
mode = "0444";
source = "/etc/nixos/static/fudo_ca.pem";
}; };
}; };
security.acme.certs.${cfg.hostname}.email = fudo-cfg.admin-email;
services.nginx = mkIf cfg.monitoring { services.nginx = mkIf cfg.monitoring {
enable = true; enable = true;
@ -71,14 +36,15 @@ in rec {
proxy_set_header Host $host; proxy_set_header Host $host;
''; '';
trusted-network-string = trusted-network-string =
optionalString ((length fudo-cfg.local-networks) > 0) optionalString ((length config.instance.local-networks) > 0)
(concatStringsSep "\n" (concatStringsSep "\n"
(map (network: "allow ${network};") fudo-cfg.local-networks)) + '' (map (network: "allow ${network};")
config.instance.local-networks)) + ''
deny all;''; deny all;'';
in { in {
"${cfg.hostname}" = { "${cfg.mail-hostname}" = {
enableACME = true; enableACME = true;
forceSSL = true; forceSSL = true;
@ -119,7 +85,9 @@ in rec {
autoStart = true; autoStart = true;
bindMounts = { bindMounts = let
cert-copies = config.fudo.acme.host-domains.${hostname}.${cfg.mail-hostname}.local-copies;
in {
"${container-maildir}" = { "${container-maildir}" = {
hostPath = cfg.mail-directory; hostPath = cfg.mail-directory;
isReadOnly = false; isReadOnly = false;
@ -134,29 +102,73 @@ in rec {
hostPath = "/etc/${container-shared}"; hostPath = "/etc/${container-shared}";
isReadOnly = true; isReadOnly = true;
}; };
"/run/mail/certs/postfix/cert.pem" = {
hostPath = cert-copies.postfix.certificate;
isReadOnly = true;
}; };
"/run/mail/certs/postfix/key.pem" = {
hostPath = cert-copies.postfix.private-key;
isReadOnly = true;
};
"/run/mail/certs/dovecot/cert.pem" = {
hostPath = cert-copies.dovecot.certificate;
isReadOnly = true;
};
"/run/mail/certs/dovecot/key.pem" = {
hostPath = cert-copies.dovecot.private-key;
isReadOnly = true;
};
};
imports = let
initialize-host = import ../../initialize-host.nix;
build-timestamp = config.instance.build-timestamp;
site = config.instance.site;
domain = config.instance.domain;
profile = "container";
in [
(initialize-host {
inherit
lib
pkgs
build-timestamp
site
domain
profile;
hostname = "mail-container";
})
];
config = { config, pkgs, ... }: { config = { config, pkgs, ... }: {
environment.systemPackages = with pkgs; [ nmap ]; environment.etc = {
"mail-server/postfix/cert.pem" = {
imports = [ ./mail.nix ]; source = "/run/mail/certs/postfix/cert.pem";
user = config.services.postfix.user;
environment = { mode = "0444";
etc = { };
"postfix-certs/key.pem" = { "mail-server/postfix/key.pem" = {
source = "/etc/${container-postfix-key}"; source = "/run/mail/certs/postfix/key.pem";
user = config.services.postfix.user; user = config.services.postfix.user;
mode = "0400"; mode = "0400";
}; };
"mail-server/dovecot/cert.pem" = {
"dovecot-certs/key.pem" = { source = "/run/mail/certs/dovecot/cert.pem";
source = "/etc/${container-dovecot-key}"; user = config.services.dovecot.user;
user = config.services.dovecot2.user; mode = "0444";
};
"mail-server/dovecot/key.pem" = {
source = "/run/mail/certs/dovecot/key.pem";
user = config.services.dovecot.user;
mode = "0400"; mode = "0400";
}; };
}; };
};
imports = [ ./mail.nix ];
fudo.mail-server = { fudo.mail-server = {
enable = true; enable = true;
@ -169,14 +181,15 @@ in rec {
state-directory = container-statedir; state-directory = container-statedir;
mail-directory = container-maildir; mail-directory = container-maildir;
postfix.ssl-certificate = "/etc/${container-postfix-cert}"; postfix = {
postfix.ssl-private-key = "/etc/postfix-certs/key.pem"; ssl-certificate = "/etc/mail-server/postfix/cert.pem";
ssl-private-key = "/etc/mail-server/postfix/key.pem";
};
dovecot = { dovecot = {
ssl-certificate = "/etc/${container-dovecot-cert}"; ssl-certificate = "/etc/mail-server/dovecot/cert.pem";
ssl-private-key = "/etc/dovecot-certs/key.pem"; ssl-private-key = "/etc/mail-server/dovecot/key.pem";
ldap = { ldap = {
# ca = "/etc/${container-fudo-ca-cert}";
server-urls = cfg.dovecot.ldap.server-urls; server-urls = cfg.dovecot.ldap.server-urls;
reader-dn = cfg.dovecot.ldap.reader-dn; reader-dn = cfg.dovecot.ldap.reader-dn;
reader-passwd = cfg.dovecot.ldap.reader-passwd; reader-passwd = cfg.dovecot.ldap.reader-passwd;

View File

@ -21,11 +21,18 @@ in {
description = "The main and default domain name for this email server."; description = "The main and default domain name for this email server.";
}; };
hostname = mkOption { mail-hostname = mkOption {
type = types.str; type = types.str;
description = "The domain name to use for the mail server."; description = "The domain name to use for the mail server.";
}; };
ldap-url = mkOption {
type = types.str;
description = "URL of the LDAP server to use for authentication.";
example = "ldaps://auth.fudo.org/";
};
monitoring = mkEnableOption "Enable monitoring for the mail server."; monitoring = mkEnableOption "Enable monitoring for the mail server.";
mail-user = mkOption { mail-user = mkOption {

View File

@ -4,7 +4,6 @@ with lib;
let let
inherit (lib.strings) concatStringsSep; inherit (lib.strings) concatStringsSep;
cfg = config.fudo.prometheus; cfg = config.fudo.prometheus;
fudo-cfg = config.fudo.common;
in { in {
@ -76,9 +75,6 @@ in {
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
security.acme.certs.${cfg.hostname}.email = fudo-cfg.admin-email;
services.nginx = { services.nginx = {
enable = true; enable = true;
@ -111,7 +107,8 @@ in {
webExternalUrl = "https://${cfg.hostname}"; webExternalUrl = "https://${cfg.hostname}";
listenAddress = "127.0.0.1:9090"; listenAddress = "127.0.0.1";
port = 9090;
scrapeConfigs = [ scrapeConfigs = [
{ {

View File

@ -9,7 +9,10 @@ let
webmail-user = cfg.user; webmail-user = cfg.user;
webmail-group = cfg.group; webmail-group = cfg.group;
base-data-path = "/var/run/rainloop"; base-data-path = "/run/rainloop";
concatMapAttrs = f: attrs:
foldr (a: b: a // b) {} (mapAttrsToList f attrs);
fastcgi-conf = builtins.toFile "fastcgi.conf" '' fastcgi-conf = builtins.toFile "fastcgi.conf" ''
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
@ -301,7 +304,7 @@ in {
site-config-file = builtins.toFile "${site}-rainloop.cfg" site-config-file = builtins.toFile "${site}-rainloop.cfg"
(import ./include/rainloop.nix lib site site-cfg site-pkgs.${site}.version); (import ./include/rainloop.nix lib site site-cfg site-pkgs.${site}.version);
domain-cfg-file = builtins.toFile "${site}-domain.cfg" '' domain-config-file = builtins.toFile "${site}-domain.cfg" ''
imap_host = "${site-cfg.mail-server}" imap_host = "${site-cfg.mail-server}"
imap_port = 143 imap_port = 143
imap_secure = "TLS" imap_secure = "TLS"

View File

@ -1,16 +1,24 @@
{ lib, ... }: { lib, ... }:
with lib; with lib;
{ let
base-host-config = host-path: let
hostname-from-file = filename: builtins.replaceStrings [".nix"] [""] filename; hostname-from-file = filename: builtins.replaceStrings [".nix"] [""] filename;
is-nix-file = filename: type: (builtins.match ".+\.nix$" filename) != null; is-nix-file = filename: type: (builtins.match ".+\.nix$" filename) != null;
is-regular-file = filename: type: type == "regular" || type == "link"; is-regular-file = filename: type: type == "regular" || type == "link";
host-files = attrNames (filterAttrs is-nix-file (filterAttrs is-regular-file (builtins.readDir host-path))); host-files = host-path:
hosts = map hostname-from-file host-files; attrNames
(filterAttrs is-nix-file
(filterAttrs is-regular-file
(builtins.readDir host-path)));
hosts = host-path:
map hostname-from-file (host-files host-path);
in {
base-host-config = host-path: let
load-host-file = hostname: import (host-path + "/${hostname}.nix"); load-host-file = hostname: import (host-path + "/${hostname}.nix");
in genAttrs hosts (hostname: load-host-file hostname); in genAttrs (hosts host-path) (hostname: load-host-file hostname);
host-list = host-path: hosts host-path;
} }

View File

@ -4,8 +4,6 @@ with lib;
let let
cfg = config.informis.cl-gemini; cfg = config.informis.cl-gemini;
lisp-helper = import ../lisp.nix { inherit pkgs; };
feedOpts = { ... }: with types; { feedOpts = { ... }: with types; {
options = { options = {
url = mkOption { url = mkOption {
@ -163,7 +161,7 @@ in {
GEMINI_TEXTFILES_ROOT = cfg.textfiles-archive; GEMINI_TEXTFILES_ROOT = cfg.textfiles-archive;
GEMINI_FEEDS = "${generate-feeds cfg.feeds}"; GEMINI_FEEDS = "${generate-feeds cfg.feeds}";
CL_SOURCE_REGISTRY = "${lisp-helper.lisp-source-registry pkgs.cl-gemini}"; CL_SOURCE_REGISTRY = "${pkgs.lib.fudo.lisp.lisp-source-registry pkgs.cl-gemini}";
}; };
path = with pkgs; [ path = with pkgs; [

View File

@ -51,6 +51,11 @@ in {
type = attrsOf (submodule user.userOpts); type = attrsOf (submodule user.userOpts);
description = "List of users who should have access to the local host"; description = "List of users who should have access to the local host";
}; };
build-seed = mkOption {
type = str;
description = "Seed used to generate configuration.";
};
}; };
config = let config = let
@ -86,15 +91,21 @@ in {
config.fudo.domains.${local-domain}.local-networks // config.fudo.domains.${local-domain}.local-networks //
config.fudo.sites.${local-site}.local-networks; config.fudo.sites.${local-site}.local-networks;
local-profile = host.profile;
build-seed = builtins.readFile config.fudo.secrets.files.build-seed;
in { in {
instance = { instance = {
local-domain = local-domain; inherit
local-site = local-site; build-seed
local-users = local-users; local-domain
local-admins = local-admins; local-site
local-groups = local-groups; local-users
local-hosts = local-hosts; local-admins
local-profile = host.profile; local-groups
local-hosts
local-profile;
}; };
}; };
} }

View File

@ -1,6 +1,6 @@
{ lib, ... }: { pkgs, ... }:
with lib; with pkgs.lib;
let let
pow = x: e: if (e == 0) then 1 else x * (pow x (e - 1)); pow = x: e: if (e == 0) then 1 else x * (pow x (e - 1));

View File

@ -1,8 +1,7 @@
{ pkgs, ... }: { pkgs, ... }:
with pkgs.lib; with pkgs.lib;
let rec {
in rec {
gather-dependencies = pkg: unique (pkg.propagatedBuildInputs ++ (concatMap gather-dependencies pkg.propagatedBuildInputs)); gather-dependencies = pkg: unique (pkg.propagatedBuildInputs ++ (concatMap gather-dependencies pkg.propagatedBuildInputs));
lisp-source-registry = pkg: concatStringsSep ":" (map (p: "${p}//") (gather-dependencies pkg)); lisp-source-registry = pkg: concatStringsSep ":" (map (p: "${p}//") (gather-dependencies pkg));

View File

@ -1,10 +1,12 @@
(final: prev: let (final: prev: {
ip = import ./ip.nix { lib = prev.lib; };
dns = import ./dns.nix { lib = prev.lib; };
in {
lib = prev.lib // { lib = prev.lib // {
fudo = { fudo = let
inherit ip dns; lib = prev.lib;
in {
ip = import ./ip.nix { pkgs = prev; };
dns = import ./dns.nix { pkgs = prev;};
passwd = import ./passwd.nix { pkgs = prev;};
lisp = import ./lisp.nix { pkgs = prev;};
}; };
}; };
}) })

View File

@ -1,6 +1,6 @@
{ lib, ... }: { pkgs, ... }:
with lib; with pkgs.lib;
let let
hash-ldap-passwd-pkg = name: passwd-file: pkgs.stdenv.mkDerivation { hash-ldap-passwd-pkg = name: passwd-file: pkgs.stdenv.mkDerivation {
name = "${name}-ldap-passwd"; name = "${name}-ldap-passwd";
@ -14,7 +14,7 @@ let
''; '';
installPhase = '' installPhase = ''
mkdir $out mkdir -p $out
mv ldap-passwd $out mv ldap-passwd $out
''; '';
}; };
@ -26,17 +26,26 @@ let
generate-random-passwd = name: length: pkgs.stdenv.mkDerivation { generate-random-passwd = name: length: pkgs.stdenv.mkDerivation {
name = "${name}-random-passwd"; name = "${name}-random-passwd";
phases = [ "buildPhase" "installPhase" ]; phases = [ "installPhase" ];
buildInputs = with pkgs; [ pwgen ]; buildInputs = with pkgs; [ pwgen ];
buildPhase = '' installPhase = ''
pwgen --symbols --num-passwords=1 ${length} > passwd pwgen --secure --num-passwords=1 ${length} > $out
''; '';
};
generate-stablerandom-passwd = name: { seed, length ? 20, ... }:
pkgs.stdenv.mkDerivation {
name = "${name}-stablerandom-passwd";
phases = [ "installPhase" ];
buildInputs = with pkgs; [ pwgen ];
installPhase = '' installPhase = ''
mkdir $out echo "${name}-${seed}" > seedfile
mv passwd $out pwgen --secure --num-passwords=1 -H seedfile ${toString length} > $out
''; '';
}; };
@ -44,5 +53,8 @@ in {
hash-ldap-passwd = hash-ldap-passwd; hash-ldap-passwd = hash-ldap-passwd;
random-passwd-file = name: length: random-passwd-file = name: length:
toPath "${generate-random-passwd name length}/passwd"; builtins.toPath "${generate-random-passwd name length}";
stablerandom-passwd-file = name: seed:
builtins.toPath "${generate-stablerandom-passwd name { seed = seed; }}";
} }

View File

@ -1,7 +1,10 @@
{ lib, ... }: { lib, ... }:
with lib; with lib;
rec { let
passwd = import ../passwd.nix { inherit lib; };
in rec {
encryptedFSOpts = { ... }: let encryptedFSOpts = { ... }: let
mountpoint = { mp, ... }: { mountpoint = { mp, ... }: {
options = with types; { options = with types; {
@ -31,6 +34,12 @@ rec {
''; '';
default = [ ]; default = [ ];
}; };
world-readable = mkOption {
type = bool;
description = "Whether to leave the top level world-readable.";
default = true;
};
}; };
}; };
in { in {
@ -81,7 +90,9 @@ rec {
}; };
}; };
hostOpts = { hostname, ... }: { hostOpts = { name, ... }: let
hostname = name;
in {
options = with types; { options = with types; {
master-key = mkOption { master-key = mkOption {
type = nullOr (submodule masterKeyOpts); type = nullOr (submodule masterKeyOpts);
@ -284,6 +295,11 @@ rec {
description = "Configuration parameters to set up initrd SSH network."; description = "Configuration parameters to set up initrd SSH network.";
default = null; default = null;
}; };
backplane-password-file = mkOption {
options = path;
description = "File containing the password used by this host to connect to the backplane.";
};
}; };
}; };
} }

View File

@ -2,12 +2,12 @@
with lib; with lib;
rec { rec {
systemUserOpts = { username, ... }: { systemUserOpts = { name, ... }: {
options = with lib.types; { options = with lib.types; {
username = mkOption { username = mkOption {
type = str; type = str;
description = "The system user's login name."; description = "The system user's login name.";
default = username; default = name;
}; };
description = mkOption { description = mkOption {
@ -23,7 +23,9 @@ rec {
}; };
}; };
userOpts = { username, ... }: { userOpts = { name, ... }: let
username = name;
in {
options = with lib.types; { options = with lib.types; {
username = mkOption { username = mkOption {
type = str; type = str;