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
./hosts.nix
./networks.nix
./profile.nix
./sites.nix
./users.nix
./wireless-networks.nix

View File

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

View File

@ -10,10 +10,17 @@ let
host-fqdn = "${hostname}.${domain-name}";
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";
acme-certificate = hostname: "/var/lib/acme/${hostname}/fullchain.pem";
letsencrypt-full-chain = name: chain: pkgs.stdenv.mkDerivation {
name = "${name}-letsencrypt-full-chain.pem";
phases = "installPhase";
installPhase = ''
cat ${chain} > $out
cat ${pkgs.letsencrypt-ca}/ca.pem >> $out
'';
};
in {
imports = let
@ -28,9 +35,66 @@ in {
in nix-files ./france;
config = {
security.acme.email = "admin@fudo.org";
fudo = {
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 = {
enable = true;
ipv4 = true;
@ -40,16 +104,53 @@ in {
};
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-directory = "/state/mail-server/mail";
state-directory = "/state/mail-server/var";
mail-directory = "/srv/mail/mailboxes";
state-directory = "/srv/mail/var";
ldap-server-urls = [
"ldap://france.fudo.org"
];
};
webmail = {
# TODO: this is not using the database!
mail-server = mail-hostname;
database.hostname = "localhost";
};
@ -58,6 +159,15 @@ in {
repository-directory = "/state/gitea/repo";
state-directory = "/state/gitea/state";
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,23 +181,27 @@ in {
};
networking = {
intif0 = {
ipv4.addresses = [{
address = "192.168.11.1";
prefixLength = 24;
}];
};
extif0 = {
ipv4.addresses = [
{
address = primary-ip;
prefixLength = 28;
}
{
address = git-server-ip;
prefixLength = 32;
}
];
useDHCP = false;
interfaces = {
intif0 = {
ipv4.addresses = [{
address = "192.168.11.1";
prefixLength = 24;
}];
};
extif0 = {
ipv4.addresses = [
{
address = primary-ip;
prefixLength = 28;
}
{
address = git-server-ip;
prefixLength = 32;
}
];
};
};
};
@ -95,7 +209,7 @@ in {
nginx = {
enable = true;
recommendedGzipSettings = true;
recommendedOptimisations = true;
recommendedOptimisation = true;
recommendedTlsSettings = true;
recommendedProxySettings = true;

View File

@ -7,83 +7,79 @@ let
site-name = config.instance.local-site;
fqdn = "${hostname}.${domain-name}";
secrets = config.fudo.secrets.host-secrets.france;
# same as genAttr, but takes back attrsets and merges them
concatGenAttrs = lst: f:
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 {
options.france = with types; {
options.fudo.france = with types; {
ldap = {
ssl-certificate = mkOption {
type = path;
type = str;
description = "SSL certificate to use for the LDAP server.";
};
ssl-private-key = mkOption {
type = path;
type = str;
description = "SSL private key to use for the LDAP server.";
};
ssl-ca-certificate = mkOption {
type = path;
type = str;
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 = {
state-directory = mkOption {
type = str;
description = "Path at which to store kerberos state.";
default = "/state/kerberos";
};
master-key-file = mkOption {
type = str;
description = "Heimdal database master key file.";
};
listen-ips = mkOption {
type = listOf str;
description = "IP addresses on which to listen for connections.";
};
};
};
config = {
fudo = {
secrets.host-secrets.${hostname} = {
ldap-ssl-certificate = {
source-file = cfg.ssl-certificate;
target-file = "/var/run/ldap/ssl-certificate.pem";
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";
};
secrets.host-secrets.${hostname}.kdc-master-key = {
source-file = cfg.kdc.master-key-file;
target-file = "/run/kerberos/kdc/master.key";
user = config.fudo.auth.kdc.user;
};
auth = {
ldap = {
ldap-server = {
enable = true;
base = "dc=fudo,dc=org";
organization = "Fudo";
rootpw-file = secrets.ldap-root-passwd;
rootpw-file = cfg.ldap.root-password-file;
kerberos-host = fqdn;
kerberos-keytab = secrets.ldap-keytab;
sslCert =
secrets.ldap-ssl-certificate.target-file;
sslKey =
secrets.ldap-ssl-private-key.target-file;
sslCACert =
secrets.ldap-ssl-ca-certificate.target-file;
kerberos-keytab = cfg.ldap.keytab;
ssl-certificate = cfg.ldap.ssl-certificate;
ssl-private-key = cfg.ldap.ssl-private-key;
ssl-ca-certificate = cfg.ldap.ssl-ca-certificate;
listen-uris = [ "ldap:///" "ldaps:///" "ldapi:///" ];
@ -95,9 +91,9 @@ in {
# TODO: let build hosts create keys?
kdc = {
enable = true;
realm = config.domains.${domain-name}.gssapi-realm;
state-directory = cfg.state-directory;
master-key-file = cfg.master-key-file;
realm = config.fudo.domains.${domain-name}.gssapi-realm;
state-directory = cfg.kdc.state-directory;
master-key-file = secrets.kdc-master-key.target-file;
acl = let
admin-entries = concatGenAttrs
config.instance.local-admins
@ -109,7 +105,7 @@ in {
"host/*.fudo.org" = { perms = [ "add" ]; };
"pam_migrate/*.fudo.org" = { perms = [ "add" "change-password" ]; };
} // 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};
cfg = config.fudo.france.git;
sshOpts = { ... }: {
options = {
options = with types; {
listen-ip = mkOption {
type = str;
description = "IP address on which to listen for SSH connections.";
};
listen-port = mkOption {
type = str;
type = port;
description = "Port on which to listen for SSH connections.";
default = 22;
};
@ -23,7 +25,7 @@ let
in {
options.france.git = with types; {
options.fudo.france.git = with types; {
repository-directory = mkOption {
type = str;
description = "Path to store git repositories.";
@ -43,6 +45,14 @@ in {
};
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 = {
databases.fudo_git.users =
config.instance.local_admins;

View File

@ -4,24 +4,108 @@ with lib;
let
hostname = config.instance.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 {
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 = {
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 = {
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 = {
LDAP_PASSWORD = secrets.jabber-ldap-password.target-file;
LDAP_PASSWORD = secrets.ldap-password.target-file;
};
sites = {
"fudo.im" = {
site-config = {
auth_method = "ldap";
ldap_servers = [ "auth.fudo.org" ];
ldap_servers = cfg.jabber.ldap-servers;
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_base = "ou=members,dc=fudo,dc=org";
ldap_filter = "(objectClass=posixAccount)";
@ -69,7 +153,7 @@ in {
"backplane.fudo.org" = {
site-config = {
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;
auth_use_cache = true;
@ -100,7 +184,6 @@ in {
mod_time = {};
mod_version = {};
};
};
};
};

View File

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

View File

@ -5,14 +5,28 @@ let
hostname = config.instance.hostname;
secrets = config.fudo.secrets.host-secrets.${hostname};
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 = {
enable = true;
local-networks = config.instance.local-networks;
admin-users = config.instance.admin-users;
ssl-private-key = secrets.postgres-ssl-key;
ssl-certificate = secrets.postgres-ssl-certificate;
keytab = secrets.postgres-keytab.target-file;
ssl-private-key = cfg.ssl-private-key;
ssl-certificate = cfg.ssl-certificate;
keytab = cfg.keytab;
};
}

View File

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

View File

@ -22,6 +22,8 @@ let
secrets = config.fudo.secrets.host-secrets.procul;
passwd = pkgs.lib.fudo.passwd;
in {
networking = {
dhcpcd.enable = false;
@ -85,75 +87,17 @@ in {
fudo = {
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 = config.fudo.secrets.files;
files = config.fudo.secrets.files;
in {
postgres-keytab = {
source-file = secrets.service-keytabs.procul.postgres;
source-file = files.service-keytabs.procul.postgres;
target-file = "/srv/postgres/secure/postgres.keytab";
user = "root";
};
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";
user = config.fudo.git.user;
};

View File

@ -1,8 +1,18 @@
{ config, lib, pkgs, ... }:
with lib;
let
syslib = pkgs.callPackage ../lib/hosts.nix {};
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, ... }:
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, ... }:
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, ... }:
with lib; {
imports = [ ./common-ui.nix ];
config = { networking = { networkmanager.enable = mkForce false; }; };
}

View File

@ -2,31 +2,4 @@
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, ... }:
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" ];
};
# NOTE: these are pointing directly to nostromo so the krb lookup works
"/net/documents" = {
device = "sea-store.sea.fudo.org:/export/documents";
device = "nostromo.sea.fudo.org:/export/documents";
fsType = "nfs4";
options = [ "comment=systemd.automount" "sec=krb5p" ];
options = [ "comment=systemd.automount" "sec=krb5p" "proto=tcp" ];
};
"/net/downloads" = {
device = "sea-store.sea.fudo.org:/export/downloads";
device = "nostromo.sea.fudo.org:/export/downloads";
fsType = "nfs4";
options = [ "comment=systemd.automount" "sec=krb5i" ];
options = [ "comment=systemd.automount" "sec=krb5i" "proto=tcp" ];
};
"/net/projects" = {
device = "sea-store.sea.fudo.org:/export/projects";
device = "nostromo.sea.fudo.org:/export/projects";
fsType = "nfs4";
options = [ "comment=systemd.automount" "sec=krb5p" ];
options = [ "comment=systemd.automount" "sec=krb5p" "proto=tcp" ];
};
};
systemd = {
tmpfiles.rules = [
"d /net/documents - root sea-documents - -"
"d /net/downloads - root sea-downloads - -"
"d /net/projects - root sea-projects - -"
];
## This fails if the filesystems already exist
# tmpfiles.rules = [
# "d /net/documents - root sea-documents - -"
# "d /net/downloads - root sea-downloads - -"
# "d /net/projects - root sea-projects - -"
# ];
# 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
# 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
join-lines = concatStringsSep "\n";

View File

@ -1,16 +1,18 @@
{ lib, ... }:
let
ip = import ./ip.nix { inherit lib; };
dns = import ./dns.nix { inherit lib; };
passwd = import ./passwd.nix { inherit lib; };
in
# NOTE: OBSOLETE! See overlay.nix
{
lib.overlays = [
(final: prev:
prev.lib // {
fudo = {
inherit ip dns passwd;
fudo = let
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; {
email = mkOption {
type = str;
@ -45,8 +47,12 @@ let
};
local-copies = let
localCopyOpts = { copy, ... }: {
options = with types; {
localCopyOpts = { name, ... }: let
copy = name;
in {
options = with types; let
target-path = "/var/run/${domain}/${copy}";
in {
user = mkOption {
type = str;
description = "User to which this copy belongs.";
@ -58,17 +64,35 @@ let
default = null;
};
path = mkOption {
type = str;
description = "Path at which to store the local copy.";
default = "/var/run/${domain}/${copy}";
};
service = mkOption {
type = str;
description = "systemd job to copy certs.";
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 {
@ -92,7 +116,7 @@ let
cfg.host-domains.${hostname} else {};
optionalStringOr = str: default:
if cond then str else default;
if (str != null) then str else default;
in {
options.fudo.acme = with types; {
@ -113,26 +137,41 @@ in {
tmpfiles.rules = let
copies = concatMapAttrs (domain: domainOpts:
domainOpts.local-copies) localDomains;
perms = copyOpts: if (copyOpts.group != null) then "0550" else "0500";
copy-paths = mapAttrsToList (copy: copyOpts:
"D '${path}' 0550 ${copyOpts.user} ${optionalStringOr copyOpts.group "-"} - -")
copies;
in copy-paths;
let
dir-entry = copyOpts: file: "D '${dirOf file}' ${perms copyOpts} ${copyOpts.user} ${optionalStringOr copyOpts.group "-"} - -";
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:
mapAttrs' (copy: copyOpts: let
key-perms = copyOpts: if (copyOpts.group != null) then "0440" else "0400";
source = config.security.acme.certs.${domain}.directory;
target = copyOpts.path;
install-certs = pkgs.writeShellScript "fudo-install-${domain}-${site}-certs.sh" ''
for cert in cert chain fullchain full key; do
cp ${source}/$cert.pem ${target}/$cert.pem
chmod 0440 ${source}/$cert.pem
done
install-certs = pkgs.writeShellScript "fudo-install-${domain}-${copy}-certs.sh" ''
cp cert.pem ${copyOpts.certificate}
chmod 0444 ${copyOpts.certificate}
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" ''
for cert in cert chain fullchain full key; do
rm -rf ${target}/$cert.pem
done
remove-certs = pkgs.writeShellScript "fudo-remove-${domain}-${copy}-certs.sh" ''
rm -f ${copyOpts.private-key}
rm -f ${copyOpts.chainy}
rm -f ${copyOpts.full-certificate}
rm -f ${copyOpts.certificate}
'';
in nameValuePair
(rm-service-ext copyOpts.service) {

View File

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

View File

@ -1,63 +1,10 @@
{ config, pkgs, lib, ... }:
with lib;
let
cfg = config.fudo.backplane.dns;
lisp-pkgs = with pkgs.localLispPackages; [
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";
powerdns-conf-dir = "${cfg.powerdns-home}/conf.d";
backplaneOpts = { ... }: {
options = {
@ -117,53 +64,59 @@ let
};
in {
options.fudo.backplane.dns = {
options.fudo.backplane.dns = with types; {
enable = mkEnableOption "Enable backplane dynamic DNS server.";
port = mkOption {
type = types.port;
type = port;
description = "Port on which to serve authoritative DNS requests.";
default = 53;
};
listen-v4-addresses = mkOption {
type = with types; listOf str;
type = listOf str;
description = "IPv4 addresses on which to listen for dns requests.";
default = [ "0.0.0.0" ];
};
listen-v6-addresses = mkOption {
type = with types; listOf str;
type = listOf str;
description = "IPv6 addresses on which to listen for dns requests.";
example = [ "[abcd::1]" ];
default = [ ];
};
required-services = mkOption {
type = with types; listOf str;
type = listOf str;
description =
"A list of services required before the DNS server can start.";
};
user = mkOption {
type = types.str;
type = str;
description = "User as which to run DNS backplane listener service.";
default = "backplane-dns";
};
group = mkOption {
type = types.str;
type = str;
description = "Group as which to run DNS backplane listener service.";
default = "backplane-dns";
};
database = mkOption {
type = with types; submodule databaseOpts;
type = submodule databaseOpts;
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 {
type = with types; submodule backplaneOpts;
type = submodule backplaneOpts;
description = "Backplane Jabber settings for the DNS server.";
};
};
@ -177,7 +130,11 @@ in {
createHome = true;
home = "/var/home/${cfg.user}";
};
backplane-powerdns = { isSystemUser = true; };
backplane-powerdns = {
isSystemUser = true;
home = cfg.powerdns-home;
createHome = true;
};
};
groups = {
@ -186,140 +143,107 @@ in {
};
};
fudo.system.services = {
backplane-powerdns-config-generator = {
description =
"Generate postgres configuration for backplane DNS server.";
requires = cfg.required-services;
type = "oneshot";
restartIfChanged = true;
partOf = [ "backplane-dns.target" ];
readWritePaths = [ powerdns-conf-dir ];
preStart = ''
mkdir -p ${powerdns-conf-dir}
chown backplane-powerdns:backplane-powerdns ${powerdns-conf-dir}
'';
# This builds the config in a bash script, to avoid storing the password
# in the nix store at any point
script = ''
if [ ! -d ${powerdns-conf-dir} ]; then
mkdir ${powerdns-conf-dir}
fi
TMPDIR=$(${pkgs.coreutils}/bin/mktemp -d -t pdns-XXXXXXXXXX)
TMPCONF=$TMPDIR/pdns.local.gpgsql.conf
if [ ! -f ${cfg.database.password-file} ]; then
echo "${cfg.database.password-file} does not exist!"
exit 1
fi
touch $TMPCONF
chown backplane-powerdns:backplane-powerdns $TMPCONF
chmod go-rwx $TMPCONF
PASSWORD=$(cat ${cfg.database.password-file})
echo "launch+=gpgsql" >> $TMPCONF
echo "gpgsql-host=${cfg.database.host}" >> $TMPCONF
echo "gpgsql-dbname=${cfg.database.database}" >> $TMPCONF
echo "gpgsql-user=${cfg.database.username}" >> $TMPCONF
echo "gpgsql-password=$PASSWORD" >> $TMPCONF
echo "gpgsql-dnssec=yes" >> $TMPCONF
mv $TMPCONF ${powerdns-conf-dir}/pdns.local.gpgsql.conf
rm -rf $TMPDIR
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 = {
description = "Fudo DNS Backplane Server";
restartIfChanged = true;
path = with pkgs; [ backplane-dns-server ];
execStart = "launch-backplane-dns.sh";
pidFile = "/run/backplane-dns.$USERNAME.pid";
user = cfg.user;
group = cfg.group;
partOf = [ "backplane-dns.target" ];
requires = [ "postgresql.service" ];
environment = {
FUDO_DNS_BACKPLANE_XMPP_HOSTNAME = cfg.backplane.host;
FUDO_DNS_BACKPLANE_XMPP_USERNAME = cfg.backplane.role;
FUDO_DNS_BACKPLANE_XMPP_PASSWORD_FILE = cfg.backplane.password-file;
FUDO_DNS_BACKPLANE_DATABASE_HOSTNAME = cfg.backplane.database.host;
FUDO_DNS_BACKPLANE_DATABASE_NAME = cfg.backplane.database.database;
FUDO_DNS_BACKPLANE_DATABASE_USERNAME =
cfg.backplane.database.username;
FUDO_DNS_BACKPLANE_DATABASE_PASSWORD_FILE =
cfg.backplane.database.password-file;
CL_SOURCE_REGISTRY =
pkgs.lib.fudo.lisp.lisp-source-registry pkgs.backplane-dns-server;
};
};
};
systemd = {
targets = {
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 =
"Generate postgres configuration for backplane DNS server.";
requiredBy = [ "backplane-powerdns.service" ];
requires = cfg.required-services;
serviceConfig.Type = "oneshot";
restartIfChanged = true;
partOf = [ "backplane-dns.target" ];
preStart = ''
mkdir -p ${powerdns-conf-dir}
chown backplane-powerdns:backplane-powerdns ${powerdns-conf-dir}
'';
# This builds the config in a bash script, to avoid storing the password
# in the nix store at any point
script = ''
if [ ! -d ${powerdns-conf-dir} ]; then
mkdir ${powerdns-conf-dir}
fi
TMPDIR=$(${pkgs.coreutils}/bin/mktemp -d -t pdns-XXXXXXXXXX)
TMPCONF=$TMPDIR/pdns.local.gpgsql.conf
if [ ! -f ${cfg.database.password-file} ]; then
echo "${cfg.database.password-file} does not exist!"
exit 1
fi
touch $TMPCONF
chown backplane-powerdns:backplane-powerdns $TMPCONF
chmod go-rwx $TMPCONF
PASSWORD=$(cat ${cfg.database.password-file})
echo "launch+=gpgsql" >> $TMPCONF
echo "gpgsql-host=${cfg.database.host}" >> $TMPCONF
echo "gpgsql-dbname=${cfg.database.database}" >> $TMPCONF
echo "gpgsql-user=${cfg.database.username}" >> $TMPCONF
echo "gpgsql-password=$PASSWORD" >> $TMPCONF
echo "gpgsql-dnssec=yes" >> $TMPCONF
mv $TMPCONF ${powerdns-conf-dir}/pdns.local.gpgsql.conf
rm -rf $TMPDIR
exit 0
'';
};
backplane-dns = {
description = "Fudo DNS Backplane Server";
restartIfChanged = true;
serviceConfig = {
ExecStart =
"${pkgs.backplane-dns-server}/bin/launch-backplane-dns.sh";
Restart = "on-failure";
PIDFile = "/run/backplane-dns.$USERNAME.pid";
User = cfg.user;
Group = cfg.group;
StandardOutput = "journal";
};
environment = {
# LD_LIBRARY_PATH = "${pkgs.openssl_1_1.out}/lib";
FUDO_DNS_BACKPLANE_XMPP_HOSTNAME = cfg.backplane.host;
FUDO_DNS_BACKPLANE_XMPP_USERNAME = cfg.backplane.role;
FUDO_DNS_BACKPLANE_XMPP_PASSWORD_FILE = cfg.backplane.password-file;
FUDO_DNS_BACKPLANE_DATABASE_HOSTNAME = cfg.backplane.database.host;
FUDO_DNS_BACKPLANE_DATABASE_NAME = cfg.backplane.database.database;
FUDO_DNS_BACKPLANE_DATABASE_USERNAME =
cfg.backplane.database.username;
FUDO_DNS_BACKPLANE_DATABASE_PASSWORD_FILE =
cfg.backplane.database.password-file;
# CL_SOURCE_REGISTRY = "${pkgs.localLispPackages.backplane-dns}//";
CL_SOURCE_REGISTRY =
lib.concatStringsSep ":" (map (pkg: "${pkg}//") lisp-pkgs);
};
requires = cfg.required-services;
partOf = [ "backplane-dns.target" ];
wantedBy = [ "multi-user.target" ];
requries = cfg.required-services ++ [ "postgresql.service" ];
};
};
};

View File

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

View File

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

View File

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

View File

@ -23,16 +23,16 @@ let
foldr (a: b: a // b) {} (mapAttrs f attrs);
concatMapAttrsToList = f: attr:
attrValues (concatMapAttrs f attr);
concatMap (i: i) (attrValues (mapAttrs f attr));
host-domains = config.fudo.acme.host-domains.${hostname};
siteCerts = site: let
certPath = host-domains.${site}.local-copies.ejabberd.path;
cert-copy = host-domains.${site}.local-copies.ejabberd;
in [
"${certPath}/fullchain.pem"
"${certPath}/privkey.pem"
"${certPath}/chain.pem"
cert-copy.certificate
cert-copy.private-key
cert-copy.chain
];
siteCertService = site:
@ -60,13 +60,13 @@ let
hosts = attrNames cfg.sites;
listen = [{
listen = map (ip: {
port = cfg.port;
module = "ejabberd_c2s";
ip = cfg.listen-ip;
ip = ip;
starttls = true;
starttls_required = true;
}];
}) cfg.listen-ips;
certfiles = concatMapAttrsToList
(site: siteOpts:
@ -94,7 +94,6 @@ let
swapper = concatStringsSep " | " secret-swappers;
in pkgs.writeShellScript "ejabberd-generate-config.sh" ''
cat ${template} | ${swapper} > ${target}
chown ${cfg.user}:${cfg.group} ${target}
'';
cfg = config.fudo.jabber;
@ -102,6 +101,11 @@ let
in {
options.fudo.jabber = with types; {
enable = mkEnableOption "Enable ejabberd server.";
listen-ips = mkOption {
type = listOf str;
description = "IPs on which to listen for Jabber connections.";
};
port = mkOption {
type = port;
@ -122,7 +126,7 @@ in {
};
admins = mkOption {
type = str;
type = listOf str;
description = "List of admin users for the server.";
default = [];
};
@ -141,7 +145,23 @@ in {
config-file = mkOption {
type = str;
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,26 +176,50 @@ in {
};
};
fudo.acme.host-domains.${hostname} = mapAttrs (site: siteCfg:
mkIf siteCfg.enableACME {
local-copies.ejabberd = {
fudo = {
acme.host-domains.${hostname} = mapAttrs (site: siteCfg:
mkIf siteCfg.enableACME {
local-copies.ejabberd = {
user = cfg.user;
group = cfg.group;
};
}) cfg.sites;
system = let
config-dir = dirOf cfg.config-file;
in {
ensure-directories.${config-dir} = {
user = cfg.user;
group = cfg.group;
perms = "0700";
};
}) cfg.sites;
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 = {
tmpfiles.rules = [
"D '${dirOf cfg.config-file}' 0550 ${cfg.user} ${cfg.group} - -"
];
services.ejabberd = let
config-generator = enter-secrets config-file-template cfg.secret-files cfg.config-file;
in {
wants = map (site: siteCertService site) (attrNames cfg.sites);
environment = cfg.secret-files;
serviceConfig = {
ExecStartPre = mkAfter "${config-generator}";
services = {
ejabberd = {
wants = map (site: siteCertService site) (attrNames cfg.sites);
requires = [ "ejabberd-config-generator.service" ];
environment = cfg.environment;
};
};
};

View File

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

View File

@ -1,67 +1,32 @@
{ lib, config, ... }:
with lib;
let
hostname = config.instance.hostname;
cfg = config.fudo.mail-server;
container-maildir = "/var/lib/mail";
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
container-mail-user = "mailer";
container-mail-user-id = 542;
container-mail-group = "mailer";
fudo-cfg = config.fudo.common;
in rec {
options.fudo.mail-server.container = {
ldap-url = mkOption {
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
config = mkIf (cfg.enableContainer) {
# Disable postfix on this host--it'll be run in the container instead
services.postfix.enable = false;
# Copy data intended for the container to a path in /etc which can be
# bind-mounted.
environment.etc = {
"${container-postfix-cert}" = {
mode = "0444";
source = cfg.postfix.ssl-certificate;
};
"${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";
fudo.acme.host-domains.${hostname}.${cfg.mail-hostname} = {
local-copies = {
postfix = {
user = "root";
};
dovecot-cert = {
user = "root";
};
};
};
security.acme.certs.${cfg.hostname}.email = fudo-cfg.admin-email;
services.nginx = mkIf cfg.monitoring {
enable = true;
@ -71,14 +36,15 @@ in rec {
proxy_set_header Host $host;
'';
trusted-network-string =
optionalString ((length fudo-cfg.local-networks) > 0)
optionalString ((length config.instance.local-networks) > 0)
(concatStringsSep "\n"
(map (network: "allow ${network};") fudo-cfg.local-networks)) + ''
(map (network: "allow ${network};")
config.instance.local-networks)) + ''
deny all;'';
in {
"${cfg.hostname}" = {
"${cfg.mail-hostname}" = {
enableACME = true;
forceSSL = true;
@ -119,7 +85,9 @@ in rec {
autoStart = true;
bindMounts = {
bindMounts = let
cert-copies = config.fudo.acme.host-domains.${hostname}.${cfg.mail-hostname}.local-copies;
in {
"${container-maildir}" = {
hostPath = cfg.mail-directory;
isReadOnly = false;
@ -134,30 +102,74 @@ in rec {
hostPath = "/etc/${container-shared}";
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, ... }: {
environment.systemPackages = with pkgs; [ nmap ];
imports = [ ./mail.nix ];
environment = {
etc = {
"postfix-certs/key.pem" = {
source = "/etc/${container-postfix-key}";
user = config.services.postfix.user;
mode = "0400";
};
"dovecot-certs/key.pem" = {
source = "/etc/${container-dovecot-key}";
user = config.services.dovecot2.user;
mode = "0400";
};
environment.etc = {
"mail-server/postfix/cert.pem" = {
source = "/run/mail/certs/postfix/cert.pem";
user = config.services.postfix.user;
mode = "0444";
};
"mail-server/postfix/key.pem" = {
source = "/run/mail/certs/postfix/key.pem";
user = config.services.postfix.user;
mode = "0400";
};
"mail-server/dovecot/cert.pem" = {
source = "/run/mail/certs/dovecot/cert.pem";
user = config.services.dovecot.user;
mode = "0444";
};
"mail-server/dovecot/key.pem" = {
source = "/run/mail/certs/dovecot/key.pem";
user = config.services.dovecot.user;
mode = "0400";
};
};
imports = [ ./mail.nix ];
fudo.mail-server = {
enable = true;
hostname = cfg.hostname;
@ -169,14 +181,15 @@ in rec {
state-directory = container-statedir;
mail-directory = container-maildir;
postfix.ssl-certificate = "/etc/${container-postfix-cert}";
postfix.ssl-private-key = "/etc/postfix-certs/key.pem";
postfix = {
ssl-certificate = "/etc/mail-server/postfix/cert.pem";
ssl-private-key = "/etc/mail-server/postfix/key.pem";
};
dovecot = {
ssl-certificate = "/etc/${container-dovecot-cert}";
ssl-private-key = "/etc/dovecot-certs/key.pem";
ssl-certificate = "/etc/mail-server/dovecot/cert.pem";
ssl-private-key = "/etc/mail-server/dovecot/key.pem";
ldap = {
# ca = "/etc/${container-fudo-ca-cert}";
server-urls = cfg.dovecot.ldap.server-urls;
reader-dn = cfg.dovecot.ldap.reader-dn;
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.";
};
hostname = mkOption {
mail-hostname = mkOption {
type = types.str;
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.";
mail-user = mkOption {
@ -176,7 +183,7 @@ in {
./mail/clamav.nix
];
config = mkIf cfg.enable {
config = mkIf cfg.enable {
networking.firewall = {
allowedTCPPorts = [ 25 110 143 587 993 995 ];
};

View File

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

View File

@ -9,7 +9,10 @@ let
webmail-user = cfg.user;
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_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
@ -301,7 +304,7 @@ in {
site-config-file = builtins.toFile "${site}-rainloop.cfg"
(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_port = 143
imap_secure = "TLS"

View File

@ -1,16 +1,24 @@
{ lib, ... }:
with lib;
{
let
hostname-from-file = filename: builtins.replaceStrings [".nix"] [""] filename;
is-nix-file = filename: type: (builtins.match ".+\.nix$" filename) != null;
is-regular-file = filename: type: type == "regular" || type == "link";
host-files = host-path:
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
hostname-from-file = filename: builtins.replaceStrings [".nix"] [""] filename;
is-nix-file = filename: type: (builtins.match ".+\.nix$" filename) != null;
is-regular-file = filename: type: type == "regular" || type == "link";
host-files = attrNames (filterAttrs is-nix-file (filterAttrs is-regular-file (builtins.readDir host-path)));
hosts = map hostname-from-file host-files;
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
cfg = config.informis.cl-gemini;
lisp-helper = import ../lisp.nix { inherit pkgs; };
feedOpts = { ... }: with types; {
options = {
url = mkOption {
@ -163,7 +161,7 @@ in {
GEMINI_TEXTFILES_ROOT = cfg.textfiles-archive;
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; [

View File

@ -51,6 +51,11 @@ in {
type = attrsOf (submodule user.userOpts);
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
@ -86,15 +91,21 @@ in {
config.fudo.domains.${local-domain}.local-networks //
config.fudo.sites.${local-site}.local-networks;
local-profile = host.profile;
build-seed = builtins.readFile config.fudo.secrets.files.build-seed;
in {
instance = {
local-domain = local-domain;
local-site = local-site;
local-users = local-users;
local-admins = local-admins;
local-groups = local-groups;
local-hosts = local-hosts;
local-profile = host.profile;
inherit
build-seed
local-domain
local-site
local-users
local-admins
local-groups
local-hosts
local-profile;
};
};
}

View File

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

View File

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

View File

@ -1,10 +1,12 @@
(final: prev: let
ip = import ./ip.nix { lib = prev.lib; };
dns = import ./dns.nix { lib = prev.lib; };
in {
(final: prev: {
lib = prev.lib // {
fudo = {
inherit ip dns;
fudo = let
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
hash-ldap-passwd-pkg = name: passwd-file: pkgs.stdenv.mkDerivation {
name = "${name}-ldap-passwd";
@ -14,7 +14,7 @@ let
'';
installPhase = ''
mkdir $out
mkdir -p $out
mv ldap-passwd $out
'';
};
@ -26,23 +26,35 @@ let
generate-random-passwd = name: length: pkgs.stdenv.mkDerivation {
name = "${name}-random-passwd";
phases = [ "buildPhase" "installPhase" ];
phases = [ "installPhase" ];
buildInputs = with pkgs; [ pwgen ];
buildPhase = ''
pwgen --symbols --num-passwords=1 ${length} > passwd
'';
installPhase = ''
mkdir $out
mv passwd $out
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 = ''
echo "${name}-${seed}" > seedfile
pwgen --secure --num-passwords=1 -H seedfile ${toString length} > $out
'';
};
in {
hash-ldap-passwd = hash-ldap-passwd;
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, ... }:
with lib;
rec {
let
passwd = import ../passwd.nix { inherit lib; };
in rec {
encryptedFSOpts = { ... }: let
mountpoint = { mp, ... }: {
options = with types; {
@ -31,6 +34,12 @@ rec {
'';
default = [ ];
};
world-readable = mkOption {
type = bool;
description = "Whether to leave the top level world-readable.";
default = true;
};
};
};
in {
@ -81,7 +90,9 @@ rec {
};
};
hostOpts = { hostname, ... }: {
hostOpts = { name, ... }: let
hostname = name;
in {
options = with types; {
master-key = mkOption {
type = nullOr (submodule masterKeyOpts);
@ -284,6 +295,11 @@ rec {
description = "Configuration parameters to set up initrd SSH network.";
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;
rec {
systemUserOpts = { username, ... }: {
systemUserOpts = { name, ... }: {
options = with lib.types; {
username = mkOption {
type = str;
description = "The system user's login name.";
default = username;
default = name;
};
description = mkOption {
@ -23,7 +23,9 @@ rec {
};
};
userOpts = { username, ... }: {
userOpts = { name, ... }: let
username = name;
in {
options = with lib.types; {
username = mkOption {
type = str;