Added live disk flake

This commit is contained in:
niten 2022-03-16 09:49:35 -07:00
parent 101a6afcd8
commit 9c9df8c3c2
32 changed files with 1328 additions and 871 deletions

View File

@ -1,3 +1,4 @@
# OBSOLETE
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
with lib; with lib;

View File

@ -3,21 +3,19 @@
{ {
imports = [ imports = [
./aliases.nix ./aliases.nix
./backplane-client.nix # ./backplane-client.nix
./bash.nix ./bash.nix
./common.nix ./common.nix
# ./dns.nix # ./dns.nix
./domains.nix
./groups.nix ./groups.nix
./instance.nix ./instance.nix
# ./kerberos.nix # ./kerberos.nix
./services.nix
./system-users.nix ./system-users.nix
./users.nix ./users.nix
./user-config.nix ./user-config.nix
./wireless-networks.nix ./wireless-networks.nix
./zones.nix
./service/chute.nix
./service/dns.nix
./service/fudo-auth.nix
./service/jabber.nix
]; ];
} }

View File

@ -1,69 +0,0 @@
{ config, lib, pkgs, ... }:
with lib;
let
hostname = config.instance.hostname;
domain = config.instance.local-domain;
cfg = config.fudo.domains.${domain};
served-domain = cfg.primary-nameserver != null;
is-primary = hostname == cfg.primary-nameserver;
create-srv-record = port: hostname: {
port = port;
host = hostname;
};
in {
config = {
fudo.dns = mkIf is-primary (let
primary-ip = pkgs.lib.network.host-ipv4 config hostname;
all-ips = pkgs.lib.network.host-ips config hostname;
in {
enable = true;
identity = "${hostname}.${domain}";
nameservers = {
ns1 = {
ipv4-address = primary-ip;
description = "Primary ${domain} nameserver";
};
};
# Deliberately leaving out localhost so the primary nameserver
# can use a custom recursor
listen-ips = all-ips;
domains = {
${domain} = {
dnssec = true;
default-host = primary-ip;
gssapi-realm = cfg.gssapi-realm;
mx = optional (cfg.primary-mailserver != null)
cfg.primary-mailserver;
# TODO: there's no guarantee this exists...
dmarc-report-address = "dmarc-report@${domain}";
zone-definition = let
zone = config.fudo.zones.${domain};
in zone // {
srv-records = {
tcp = {
domain = [{
host = "ns1.${domain}";
port = 53;
}];
};
udp = {
domain = [{
host = "ns1.${domain}";
port = 53;
}];
};
};
};
};
};
});
};
}

View File

@ -1,5 +1,3 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
{ {}
}

View File

@ -51,6 +51,12 @@ with lib; {
fsType = "btrfs"; fsType = "btrfs";
options = [ "subvol=@state" "compress=zstd" "noatime" "nodiratime" "noexec" ]; options = [ "subvol=@state" "compress=zstd" "noatime" "nodiratime" "noexec" ];
}; };
"/var/lib/acme" = {
device = "/dev/disk/by-label/system";
fsType = "btrfs";
options = [ "subvol=@acme" "compress=zstd" "noatime" "nodiratime" "noexec" ];
};
}; };
swapDevices = [{ device = "/dev/disk/by-label/swap"; }]; swapDevices = [{ device = "/dev/disk/by-label/swap"; }];

View File

@ -55,6 +55,18 @@ with lib; {
fsType = "btrfs"; fsType = "btrfs";
options = [ "subvol=@state" "compress=zstd" "noatime" "nodiratime" "noexec" ]; options = [ "subvol=@state" "compress=zstd" "noatime" "nodiratime" "noexec" ];
}; };
"/var/lib/acme" = {
device = "/dev/disk/by-label/data";
fsType = "btrfs";
options = [ "subvol=@acme" "compress=zstd" "noatime" "nodiratime" "noexec" ];
};
"/var/lib/prometheus" = {
device = "/dev/disk/by-label/data";
fsType = "btrfs";
options = [ "subvol=@prometheus" "compress=zstd" "noatime" "nodiratime" "noexec" ];
};
}; };
swapDevices = [{ device = "/dev/disk/by-label/swap"; }]; swapDevices = [{ device = "/dev/disk/by-label/swap"; }];

View File

@ -52,7 +52,7 @@
"/home" = { "/home" = {
device = "/dev/disk/by-label/zbox-data"; device = "/dev/disk/by-label/zbox-data";
fsType = "btrfs"; fsType = "btrfs";
options = [ "noatime" "nodiratime" "compress=zstd" "noexec" "subvol=@home" ]; options = [ "noatime" "nodiratime" "compress=zstd" "subvol=@home" ];
}; };
}; };

View File

@ -54,7 +54,7 @@ in {
ssl-private-key = "${mail-ssl-dir}/key.pem"; ssl-private-key = "${mail-ssl-dir}/key.pem";
in { in {
enableContainer = true; enableContainer = true;
monitoring = true; monitoring.enable = true;
domain = domain-name; domain = domain-name;
mail-hostname = "mail.${domain-name}"; mail-hostname = "mail.${domain-name}";

View File

@ -5,6 +5,12 @@ let
shinobi-od-port = "7082"; shinobi-od-port = "7082";
state-dir = "/state"; # This must be a string! state-dir = "/state"; # This must be a string!
home-assistant-port = 8123;
parent-config = config;
generate-mac = pkgs.lib.network.generate-mac-address;
in { in {
boot = { boot = {
loader.grub.copyKernels = true; loader.grub.copyKernels = true;
@ -22,20 +28,6 @@ in {
}; };
}; };
# fudo.secrets = {
# host-secrets.lambda = {
# host-keytab = {
# source-file = /state/secrets/kerberos/lambda.keytab;
# target-file = "/etc/krb5.keytab";
# user = "root";
# };
# };
# secret-group = "fudo-secrets";
# secret-users = [ "niten" ];
# secret-paths = [ "/state/secrets" ];
# };
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
"L /root/.gnupg - - - - ${state-dir}/user/root/gnupg" "L /root/.gnupg - - - - ${state-dir}/user/root/gnupg"
"L /root/.ssh/id_rsa - - - - ${state-dir}/user/root/ssh/id_rsa" "L /root/.ssh/id_rsa - - - - ${state-dir}/user/root/ssh/id_rsa"
@ -92,6 +84,21 @@ in {
Defaults lecture = never Defaults lecture = never
''; '';
services.nginx = {
enable = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedGzipSettings = true;
virtualHosts."home.sea.fudo.org" = {
locations."/" = {
proxyPass =
"http://localhost:${toString home-assistant-port}";
proxyWebsockets = true;
};
};
};
virtualisation = { virtualisation = {
docker = { docker = {
enable = true; enable = true;
@ -100,19 +107,30 @@ in {
}; };
oci-containers = { oci-containers = {
backend = "docker";
containers = { containers = {
shinobi = { home-assistant = {
image = "shinobisystems/shinobi:latest"; image = "homeassistant/home-assistant:stable";
ports = [ "${shinobi-port}:8080" ]; autoStart = true;
environment.TZ = config.time.timeZone;
ports = [ "${toString home-assistant-port}:8123" ];
volumes = [ volumes = [
"/state/shinobi/plugins:/home/Shinobi/plugins" "/state/services/home-assistant:/config"
"/state/shinobi/config:/home/Shinobi/config"
"/state/shinobi/videos:/home/Shinobi/videos"
"/state/shinobi/db-data:/var/lib/mysql"
"/etc/localtime:/etc/localtime:ro"
]; ];
}; };
# shinobi = {
# image = "shinobisystems/shinobi:latest";
# ports = [ "${shinobi-port}:8080" ];
# volumes = [
# "/state/shinobi/plugins:/home/Shinobi/plugins"
# "/state/shinobi/config:/home/Shinobi/config"
# "/state/shinobi/videos:/home/Shinobi/videos"
# "/state/shinobi/db-data:/var/lib/mysql"
# "/etc/localtime:/etc/localtime:ro"
# ];
# };
# shinobi-od = { # shinobi-od = {
# image = "shinobisystems/shinobi-tensorflow:latest"; # image = "shinobisystems/shinobi-tensorflow:latest";
# volumes = # volumes =
@ -129,41 +147,4 @@ in {
}; };
}; };
}; };
services.nginx = {
enable = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
virtualHosts = {
"panopticon.sea.fudo.org" = {
locations."/" = {
# localhost defaults to IPv6
proxyPass = "http://127.0.0.1:${shinobi-port}/";
extraConfig = ''
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-By $server_addr:$server_port;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
'';
};
};
# "panopticon-od.sea.fudo.org" = {
# locations."/" = {
# proxyPass = "http://localhost:${shinobi-od-port}";
# extraConfig = ''
# proxy_http_version 1.1;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "Upgrade";
# '';
# };
# };
};
};
} }

View File

@ -42,19 +42,19 @@ in {
# informis.cl-gemini = { # informis.cl-gemini = {
# enable = true; # enable = true;
# hostname = "gemini.informis.land"; # hostname = "gemini.fudo.org";
# server-ip = host-ipv4; # server-ip = host-ipv4;
# document-root = "/srv/gemini/root"; # document-root = "/state/gemini/root";
# textfiles-archive = "${pkgs.textfiles}"; # # textfiles-archive = "${pkgs.textfiles}";
# slynk-port = 4005; # slynk-port = 4005;
# feeds = { # # feeds = {
# viator = { # # viator = {
# title = "viator's phlog"; # # title = "viator's phlog";
# path = "/home/viator/gemini-public/feed/"; # # path = "/home/viator/gemini-public/feed/";
# url = "gemini://informis.land/user/viator/feed/"; # # url = "gemini://informis.land/user/viator/feed/";
# }; # # };
# }; # # };
# }; # };
fudo = { fudo = {

View File

@ -10,7 +10,7 @@ let
domain-name = host-config.domain; domain-name = host-config.domain;
domain = config.fudo.domains.${domain-name}; domain = config.fudo.domains.${domain-name};
dns-proxy-port = 5335; # dns-proxy-port = 5335;
in { in {
config = { config = {
@ -29,37 +29,24 @@ in {
intif2 = { useDHCP = false; }; intif2 = { useDHCP = false; };
}; };
nameservers = [ "10.0.0.1" ]; enableIPv6 = false;
# nameservers = [ "10.0.0.1" ];
# FIXME: this should be automatic # FIXME: this should be automatic
firewall.trustedInterfaces = # firewall.trustedInterfaces =
[ "intif0" "intif1" "intif2" "lo" "docker0" ]; # [ "intif0" "intif1" "intif2" "lo" "docker0" ];
nat = { # nat = {
enable = true; # enable = true;
externalInterface = "enp1s0"; # externalInterface = "enp1s0";
internalInterfaces = [ "intif0" "intif1" "intif2" ]; # internalInterfaces = [ "intif0" "intif1" "intif2" ];
}; # };
}; };
fudo = { fudo = {
hosts.limina.external-interfaces = [ "enp1s0" ]; hosts.limina.external-interfaces = [ "enp1s0" ];
local-network = {
enable = true;
domain = domain-name;
dns-servers = [ primary-ip ];
gateway = primary-ip;
dhcp-interfaces = [ "intif0" ];
dns-listen-ips = [ primary-ip "127.0.0.1" "127.0.1.1" "::1" ];
recursive-resolver = "${primary-ip} port 5353";
network = site.network;
dhcp-dynamic-network = site.dynamic-network;
search-domains = [ domain-name "fudo.org" ];
enable-reverse-mappings = true;
zone-definition = config.fudo.zones.${domain-name};
};
client.dns.external-interface = "enp1s0"; client.dns.external-interface = "enp1s0";
garbage-collector = { garbage-collector = {
@ -67,59 +54,67 @@ in {
timing = "weekly"; timing = "weekly";
}; };
secure-dns-proxy = { services = {
local-network = {
enable = true; enable = true;
listen-port = dns-proxy-port; internal-interfaces = [ "intif0" "intif1" "intif2" ];
upstream-dns = external-interface = "enp1s0";
[ "https://1.1.1.1/dns-query" "https://1.0.0.1/dns-query" ]; dns-filter-proxy.enable = true;
bootstrap-dns = "1.1.1.1"; };
allowed-networks =
[ "1.1.1.1/32" "1.0.0.1/32" "10.0.0.0/16" "localhost" "link-local" ]; metrics = {
listen-ips = [ primary-ip ]; prometheus.state-directory = "/state/services/prometheus";
};
# wireguard-gateway = {
# enable = true;
# network = "10.0.200.0/24";
# peers = {
# niten-phone = {
# public-key = "";
# assigned-ip = "10.0.200.2";
# };
# };
# };
}; };
}; };
virtualisation = { # virtualisation = {
docker = { # docker = {
enable = true; # enable = true;
autoPrune.enable = true; # autoPrune.enable = true;
enableOnBoot = true; # enableOnBoot = true;
}; # };
oci-containers = { # oci-containers = {
backend = "docker"; # backend = "docker";
containers = { # containers = {
pihole = { # pihole = {
image = "pihole/pihole:2021.10"; # image = "pihole/pihole:2021.10";
autoStart = true; # autoStart = true;
ports = [ "5353:53/tcp" "5353:53/udp" "3080:80/tcp" ]; # ports = [ "5353:53/tcp" "5353:53/udp" "3080:80/tcp" ];
environment = { # environment = {
# ServerIP = primary-ip; # # ServerIP = primary-ip;
VIRTUAL_HOST = "dns-hole.sea.fudo.org"; # VIRTUAL_HOST = "dns-hole.sea.fudo.org";
DNS1 = "${primary-ip}#${toString dns-proxy-port}"; # DNS1 = "${primary-ip}#${toString dns-proxy-port}";
}; # };
volumes = [ # volumes = [
"/state/pihole/etc-pihole/:/etc/pihole/" # "/state/pihole/etc-pihole/:/etc/pihole/"
"/state/pihole/etc-dnsmasq.d/:/etc/dnsmasq.d/" # "/state/pihole/etc-dnsmasq.d/:/etc/dnsmasq.d/"
]; # ];
}; # };
}; # };
}; # };
}; # };
# Support for statelessness # Support for statelessness
environment.etc = { environment.etc = {
# TODO: replace with current config # TODO: replace with current config
nixos.source = "/state/nixos"; # nixos.source = "/state/nixos";
NIXOS.source = "/state/etc/NIXOS"; NIXOS.source = "/state/etc/NIXOS";
"host-config.nix".source = "/state/etc/host-config.nix"; "host-config.nix".source = "/state/etc/host-config.nix";
}; };
security.sudo.extraConfig = ''
# rollback results in sudo lectures after each reboot
Defaults lecture = never
'';
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
"L /etc/adjtime - - - - /state/etc/adjtime" "L /etc/adjtime - - - - /state/etc/adjtime"
"L /root/.gnupg - - - - /state/root/gnupg" "L /root/.gnupg - - - - /state/root/gnupg"
@ -135,73 +130,73 @@ in {
systemd.services.nginx.requires = [ "bind.service" ]; systemd.services.nginx.requires = [ "bind.service" ];
services = { services = {
nginx = { # nginx = {
enable = true; # enable = true;
recommendedGzipSettings = true; # recommendedGzipSettings = true;
recommendedOptimisation = true; # recommendedOptimisation = true;
recommendedProxySettings = true; # recommendedProxySettings = true;
virtualHosts = { # virtualHosts = {
"dns-hole.${domain-name}" = { # "dns-hole.${domain-name}" = {
serverAliases = [ # serverAliases = [
"pi-hole.${domain-name}" # "pi-hole.${domain-name}"
"pihole.${domain-name}" # "pihole.${domain-name}"
"hole.${domain-name}" # "hole.${domain-name}"
"pi-hole" # "pi-hole"
"pihole" # "pihole"
"dns-hole" # "dns-hole"
"hole" # "hole"
]; # ];
locations."/" = { proxyPass = "http://127.0.0.1:3080"; }; # locations."/" = { proxyPass = "http://127.0.0.1:3080"; };
}; # };
## This keeps failing, too many requests...give it a rest for now # ## This keeps failing, too many requests...give it a rest for now
# "sea-camera.fudo.link" = { # # "sea-camera.fudo.link" = {
# enableACME = true; # # enableACME = true;
# forceSSL = true; # # forceSSL = true;
# locations."/" = { # # locations."/" = {
# # proxyPass = "http://cargo.sea.fudo.org:5000/webman/3rdparty/SurveillanceStation/"; # # # proxyPass = "http://cargo.sea.fudo.org:5000/webman/3rdparty/SurveillanceStation/";
# proxyPass = "http://cargo.sea.fudo.org:5000/"; # # proxyPass = "http://cargo.sea.fudo.org:5000/";
# extraConfig = '' # # extraConfig = ''
# proxy_http_version 1.1; # # proxy_http_version 1.1;
# proxy_set_header Upgrade $http_upgrade; # # proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "Upgrade"; # # proxy_set_header Connection "Upgrade";
# proxy_set_header Host $host; # # proxy_set_header Host $host;
# # # proxy_set_header X-Real-IP $remote_addr;
# # # proxy_set_header X-Forwarded-By $server_addr:$server_port;
# # # proxy_set_header X-Forwarded-For $remote_addr;
# # # proxy_set_header X-Forwarded-Proto $scheme;
# # '';
# # };
# # };
# # "sea-camera-od.fudo.link" = {
# # enableACME = true;
# # forceSSL = true;
# # locations."/" = {
# # proxyPass = "http://panopticon-od.sea.fudo.org";
# # extraConfig = ''
# # proxy_http_version 1.1;
# # proxy_set_header Upgrade $http_upgrade;
# # proxy_set_header Connection "Upgrade";
# # proxy_set_header Host $host;
# # proxy_set_header X-Real-IP $remote_addr; # # proxy_set_header X-Real-IP $remote_addr;
# # proxy_set_header X-Forwarded-By $server_addr:$server_port; # # proxy_set_header X-Forwarded-By $server_addr:$server_port;
# # proxy_set_header X-Forwarded-For $remote_addr; # # proxy_set_header X-Forwarded-For $remote_addr;
# # proxy_set_header X-Forwarded-Proto $scheme; # # proxy_set_header X-Forwarded-Proto $scheme;
# ''; # # '';
# # };
# # };
# }; # };
# }; # };
# "sea-camera-od.fudo.link" = {
# enableACME = true;
# forceSSL = true;
# locations."/" = {
# proxyPass = "http://panopticon-od.sea.fudo.org";
# extraConfig = ''
# proxy_http_version 1.1;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "Upgrade";
# proxy_set_header Host $host;
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-By $server_addr:$server_port;
# proxy_set_header X-Forwarded-For $remote_addr;
# proxy_set_header X-Forwarded-Proto $scheme;
# '';
# };
# };
};
};
openssh = { openssh = {
hostKeys = [ hostKeys = [
{ {

View File

@ -41,7 +41,11 @@ in {
}; };
}; };
systemd.services.nfs-server = { systemd = {
tmpfiles.rules = [ "d /state/services 0755 root root - -" ];
services = {
nfs-server = {
# Don't start on boot # Don't start on boot
wantedBy = mkForce [ "sea-store.target" ]; wantedBy = mkForce [ "sea-store.target" ];
# Only start after filesystem mounts are available # Only start after filesystem mounts are available
@ -52,6 +56,66 @@ in {
]; ];
}; };
grafana = {
requires = [ "postgresql.service" ];
bindsTo = [ "postgresql.service" ];
};
};
};
fudo = let
grafana-database-passwd-file = pkgs.lib.passwd.stablerandom-passwd-file
"grafana-database-nostromo-password"
"grafana-database-nostromo-password-${config.instance.build-seed}";
host-secrets = config.fudo.secrets.host-secrets.${hostname};
in {
secrets.host-secrets.${hostname} = {
grafana-database-password = {
source-file = grafana-database-passwd-file;
target-file = "/run/services/grafana/db.passwd";
user = config.systemd.services.grafana.serviceConfig.User;
};
postgres-grafana-password = {
source-file = grafana-database-passwd-file;
target-file = "/run/services/postgres/db.passwd";
user = config.services.postgresql.superUser;
};
};
services = {
logging.loki.state-directory = "/state/services/loki";
metrics.grafana = {
state-directory = "/state/services/grafana";
smtp.hostname = "mail.fudo.org";
database = {
user = "grafana";
password-file = host-secrets.grafana-database-password.target-file;
};
ldap.base-dn = "dc=fudo,dc=org";
};
};
postgresql = {
enable = true;
local-networks = config.instance.local-networks;
state-directory = "/state/services/postgresql";
databases.grafana.users = config.instance.local-admins;
users.grafana = {
password-file = host-secrets.postgres-grafana-password.target-file;
databases.grafana = {
entity-access = {
"ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
"ALL SEQUENCES IN SCHEMA public" = "ALL PRIVILEGES";
};
};
};
};
};
## Until I can figure out how to use one common host API, forget this ## Until I can figure out how to use one common host API, forget this
# fudo.ipfs = { # fudo.ipfs = {
# enable = true; # enable = true;

View File

@ -14,18 +14,21 @@ let
host-secrets = config.fudo.secrets.host-secrets.${hostname}; host-secrets = config.fudo.secrets.host-secrets.${hostname};
postgresql-user = postgresql-user = config.systemd.services.postgresql.serviceConfig.User;
config.systemd.services.postgresql.serviceConfig.User;
files = config.fudo.secrets.files; files = config.fudo.secrets.files;
acme-copies = config.fudo.acme.host-domains.${hostname}; acme-copies = config.fudo.acme.host-domains.${hostname};
grafana-database-passwd-file =
pkgs.lib.passwd.stablerandom-passwd-file "grafana-database-password"
"grafana-database-password-${config.instance.build-seed}";
in { in {
imports = [ imports = [
./nutboy3/cashew.nix ./nutboy3/cashew.nix
./nutboy3/forum_selby_ca.nix # ./nutboy3/forum_selby_ca.nix
]; ];
config = { config = {
@ -42,15 +45,25 @@ in {
}]; }];
}; };
systemd.tmpfiles.rules = [ "L /etc/adjtime - - - - /state/etc/adjtime" ]; systemd = {
tmpfiles.rules = [
"L /etc/adjtime - - - - /state/etc/adjtime"
"d /state/services 0555 - - - -"
];
services.grafana = {
bindsTo = [ "postgresql.service" ];
requires = [ "postgresql.service" ];
};
};
environment.systemPackages = local-packages; environment = { systemPackages = local-packages; };
security.acme.email = "admin@fudo.org";
fudo = { fudo = {
hosts.${hostname}.external-interfaces = [ "extif0" ]; hosts.${hostname}.external-interfaces = [ "extif0" ];
secrets.host-secrets.nutboy3 = let secrets.host-secrets.${hostname} = let files = config.fudo.secrets.files;
files = config.fudo.secrets.files;
in { in {
heimdal-master-key = { heimdal-master-key = {
source-file = files.realm-master-keys."FUDO.ORG"; source-file = files.realm-master-keys."FUDO.ORG";
@ -69,6 +82,18 @@ in {
target-file = "/run/postgresql/postgres.keytab"; target-file = "/run/postgresql/postgres.keytab";
user = postgresql-user; user = postgresql-user;
}; };
grafana-database-password = {
source-file = grafana-database-passwd-file;
target-file = "/run/metrics/grafana/db.passwd";
user = config.systemd.services.grafana.serviceConfig.User;
};
postgres-grafana-password = {
source-file = grafana-database-passwd-file;
target-file = "/run/postgres-users/grafana.passwd";
user = config.services.postgresql.superUser;
};
}; };
acme.host-domains.${hostname} = { acme.host-domains.${hostname} = {
@ -96,12 +121,11 @@ in {
services = { services = {
jabber = { jabber = {
enable = true;
domain = "jabber.fudo.org"; domain = "jabber.fudo.org";
hostname = "jabber.fudo.org";
ldap.servers = [ "nutboy3.fudo.org" ]; ldap.servers = [ "nutboy3.fudo.org" ];
state-directory = "/state/ejabberd"; state-directory = "/state/ejabberd";
}; };
auth = { auth = {
ldap.state-directory = "/state/auth/ldap"; ldap.state-directory = "/state/auth/ldap";
kerberos = { kerberos = {
@ -109,6 +133,34 @@ in {
master-key-file = host-secrets.heimdal-master-key.target-file; master-key-file = host-secrets.heimdal-master-key.target-file;
}; };
}; };
postgresql = {
state-directory = "/state/services/postgresql";
keytab =
config.fudo.secrets.files.service-keytabs.${hostname}.postgres;
};
metrics = {
prometheus.state-directory = "/state/services/prometheus";
grafana = {
state-directory = "/state/services/grafana";
database = {
user = "grafana";
password-file =
host-secrets.grafana-database-password.target-file;
};
};
};
logging.loki.state-directory = "/state/services/loki";
selby-forum = {
enable = true;
state-directory = "/state/services/selby-forum";
legacy-forum-data = files.blobs."selby-forum-2021-12-14.clean";
external-interface = "extif0";
mail.host = "mail.fudo.org";
};
}; };
# dns.state-directory = "/state/nsd"; # dns.state-directory = "/state/nsd";
@ -155,16 +207,21 @@ in {
# }; # };
# }; # };
postgresql = let postgresql = {
cert-copy = acme-copies.${host-fqdn}.local-copies.postgresql; databases.grafana.users = config.instance.local-admins;
in {
enable = true; users.grafana = {
ssl-certificate = cert-copy.full-certificate; password-file = host-secrets.postgres-grafana-password.target-file;
ssl-private-key = cert-copy.private-key; databases.grafana = {
keytab = host-secrets.postgresql-keytab.target-file; access = "CONNECT";
local-networks = config.instance.local-networks; entity-access = {
state-directory = "/state/postgresql"; "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
required-services = [ cert-copy.service config.fudo.secrets.secret-target ]; # "SELECT,INSERT,UPDATE,DELETE";
"ALL SEQUENCES IN SCHEMA public" = "ALL PRIVILEGES";
# "SELECT, UPDATE";
};
};
};
}; };
# git = { # git = {

View File

@ -6,8 +6,6 @@ let
hostname = config.instance.hostname; hostname = config.instance.hostname;
host-secrets = config.fudo.secrets.host-secrets.${hostname}; host-secrets = config.fudo.secrets.host-secrets.${hostname};
discourse-user = config.systemd.services.discourse.serviceConfig.User;
database-name = "forum_selby_ca"; database-name = "forum_selby_ca";
database-user = "forum_selby_ca"; database-user = "forum_selby_ca";
@ -35,31 +33,24 @@ let
} }
''; '';
head-pkgs = let
nixos = pkgs.fetchgit {
url = "https://github.com/nixos/nixpkgs.git";
rev = "21.11";
sha256 = "162dywda2dvfj1248afxc45kcrg83appjd0nmdb541hl7rnncf02";
};
in import "${nixos}" {
system = pkgs.system;
config = pkgs.config;
overlays = pkgs.overlays;
};
in {
config = let
admin-pw-file = "admin.passwd";
db-pw-file = "database.passwd";
data-file = "data-forum_selby_ca.txt";
in { in {
config = {
services.discourse = {
enable = true;
hostname = site;
enableACME = true;
plugins = with config.services.discourse.package.plugins; [
discourse-migratepassword
];
admin = {
username = "admin";
fullName = "Admin";
email = "admin@selby.ca";
passwordFile = host-secrets.selby-discourse-admin.target-file;
};
database = {
name = database-name;
host = "localhost";
username = database-user;
passwordFile =
host-secrets.selby-discourse-database-passwd.target-file;
};
};
fudo = { fudo = {
secrets.host-secrets.${hostname} = let secrets.host-secrets.${hostname} = let
@ -72,8 +63,8 @@ in {
in { in {
selby-discourse-database-passwd = { selby-discourse-database-passwd = {
source-file = selby-discourse-db-password; source-file = selby-discourse-db-password;
target-file = "/run/selby/forum/database.passwd"; target-file = "/run/selby/forum/${db-pw-file}";
user = discourse-user; user = "root";
}; };
postgresql-selby-discourse-password = { postgresql-selby-discourse-password = {
@ -86,14 +77,14 @@ in {
source-file = pkgs.lib.passwd.stablerandom-passwd-file source-file = pkgs.lib.passwd.stablerandom-passwd-file
"selby-discourse-admin" "selby-discourse-admin"
"selby-discourse-admin-${config.instance.build-seed}"; "selby-discourse-admin-${config.instance.build-seed}";
target-file = "/run/selby/forum/admin.passwd"; target-file = "/run/selby/forum/${admin-pw-file}";
user = discourse-user; user = "root";
}; };
selby-forum-data = { selby-forum-data = {
source-file = files.blobs."selby-forum-2021-12-14.clean"; source-file = files.blobs."selby-forum-2021-12-14.clean";
target-file = "/run/selby/forum/forum-data.txt"; target-file = "/run/selby/forum/${data-file}";
user = discourse-user; user = "root";
}; };
selby-forum-passwords-sql = { selby-forum-passwords-sql = {
@ -118,27 +109,139 @@ in {
}; };
}; };
security.acme.certs.${site}.email = "admin@selby.ca"; ## Fuckin what why won't this cert work?
services.nginx = {
enable = true;
recommendedOptimisation = true;
recommendedGzipSettings = true;
recommendedProxySettings = true;
virtualHosts = {
"${site}" = {
enableACME = true;
forceSSL = true;
locations."/".proxyPass = "http://192.168.55.2:80";
};
};
};
containers.selby-forum = let
parent-host = "192.168.55.1";
container-host = "192.168.55.2";
in {
ephemeral = true;
privateNetwork = true;
localAddress = container-host;
hostAddress = parent-host;
autoStart = true;
bindMounts = {
"/run/selby-discourse" = {
hostPath = "/run/selby/forum";
isReadOnly = true;
};
"/var/lib/discourse" = {
hostPath = state-directory;
isReadOnly = false;
};
};
config = { config, lib, ... }: let
discourse-user = config.systemd.services.discourse.serviceConfig.User;
in {
nixpkgs.pkgs = head-pkgs;
environment.systemPackages = with pkgs; [
nmap
];
networking.firewall.enable = false;
services.discourse = {
enable = true;
hostname = site;
enableACME = false;
plugins = with config.services.discourse.package.plugins; [
discourse-migratepassword
];
admin = {
username = "admin";
fullName = "Admin";
email = "admin@selby.ca";
passwordFile = "/etc/selby-discourse/${admin-pw-file}";
};
database = {
name = database-name;
host = parent-host;
username = database-user;
passwordFile = "/etc/selby-discourse/${db-pw-file}";
};
};
environment.etc = {
"selby-discourse/${admin-pw-file}" = {
source = "/run/selby-discourse/${admin-pw-file}";
user = discourse-user;
mode = "0400";
};
"selby-discourse/${db-pw-file}" = {
source = "/run/selby-discourse/${db-pw-file}";
user = discourse-user;
mode = "0400";
};
"selby-discourse/${data-file}" = {
source = "/run/selby-discourse/${data-file}";
user = discourse-user;
mode = "0400";
};
};
systemd = { systemd = {
tmpfiles.rules = [ tmpfiles.rules = [
"d ${state-directory} 750 ${discourse-user} - - -" "/run/discourse 750 ${discourse-user} ${discourse-user} - -"
"L /var/lib/discourse - - - - ${state-directory}" "/var/lib/discourse 750 ${discourse-user} ${discourse-user} - -"
]; ];
services = { services = {
discourse = { discourse = {
bindsTo = [ "postgresql.service" ]; after = [ "multi-user.target" ];
after = [
config.fudo.postgresql.systemd-target
"postgresql.service"
];
}; };
discourse-import-vanilla = let
env-without-path =
filterAttrs (attr: _: attr != "PATH")
config.systemd.services.discourse.environment;
selby-forum-data = container-selby-forum-data-file;
in {
description = "One-off job to import Vanilla forum.";
path = config.systemd.services.discourse.path;
environment = env-without-path;
serviceConfig = {
User = config.systemd.services.discourse.serviceConfig.User;
Group = config.systemd.services.discourse.serviceConfig.Group;
Type = "oneshot";
WorkingDirectory = config.systemd.services.discourse.serviceConfig.WorkingDirectory;
ExecStart = pkgs.writeShellScript "import-vanilla-forum.sh" ''
ruby script/import_scripts/vanilla.rb /etc/selby-discourse/${data-file}
'';
};
};
};
};
};
};
systemd = {
tmpfiles.rules = [
"d ${state-directory} 750 - - - -"
];
services = {
discourse-prepare = { discourse-prepare = {
description = "Do discourse's superuser-requiring database work for it."; description = "Do discourse's superuser-requiring database work for it.";
wantedBy = [ "discourse.service" ]; wantedBy = [ "container@forum-selby-ca.service" ];
before = [ "discourse.service" ]; before = [ "container@forum-selby-ca.service" ];
requires = [ config.fudo.postgresql.systemd-target ]; requires = [ config.fudo.postgresql.systemd-target ];
after = [ config.fudo.postgresql.systemd-target ]; after = [ config.fudo.postgresql.systemd-target ];
path = with pkgs; [ postgresql ]; path = with pkgs; [ postgresql ];
@ -151,34 +254,13 @@ in {
}; };
}; };
discourse-import-vanilla = let
env-without-path =
filterAttrs (attr: _: attr != "PATH")
config.systemd.services.discourse.environment;
selby-forum-data = host-secrets.selby-forum-data.target-file;
in {
description = "One-off job to import Vanilla forum.";
path = config.systemd.services.discourse.path;
environment = env-without-path;
serviceConfig = {
User = config.systemd.services.discourse.serviceConfig.User;
Group = config.systemd.services.discourse.serviceConfig.Group;
Type = "oneshot";
WorkingDirectory = config.systemd.services.discourse.serviceConfig.WorkingDirectory;
ExecStart = pkgs.writeShellScript "import-vanilla-forum.sh" ''
ruby script/import_scripts/vanilla.rb ${selby-forum-data}
'';
};
};
discourse-add-password-hash = let discourse-add-password-hash = let
alter-user-script = pkgs.writeText "create-password-column.sql" '' alter-user-script = pkgs.writeText "create-password-column.sql" ''
ALTER TABLE users ADD COLUMN IF NOT EXISTS import_pass VARCHAR (64); ALTER TABLE users ADD COLUMN IF NOT EXISTS import_pass VARCHAR (128);
''; '';
in { in {
description = "One-off job to add user password hashes from Vanilla forum."; description = "One-off job to add user password hashes from Vanilla forum.";
path = with pkgs; [ postgresql ]; path = with pkgs; [ postgresql ];
wantedBy = [ "discourse.service" ];
serviceConfig = { serviceConfig = {
User = config.services.postgresql.superUser; User = config.services.postgresql.superUser;
Type = "oneshot"; Type = "oneshot";

View File

@ -23,6 +23,11 @@ let
host-certs = config.fudo.acme.host-domains.${hostname}; host-certs = config.fudo.acme.host-domains.${hostname};
grafana-database-password =
pkgs.lib.passwd.stablerandom-passwd-file
"grafana-database-password-${hostname}"
"grafana-database-password-${hostname}-${config.instance.build-seed}";
in { in {
networking = { networking = {
dhcpcd.enable = false; dhcpcd.enable = false;
@ -55,6 +60,8 @@ in {
networking.firewall.allowedTCPPorts = [ 80 443 ]; networking.firewall.allowedTCPPorts = [ 80 443 ];
security.acme.email = "viator@informis.land";
users = { users = {
users = { users = {
gituser = { gituser = {
@ -84,17 +91,17 @@ in {
}; };
}; };
services.chute = let # services.chute = let
secret-files = config.fudo.secrets.files.service-secrets.procul; # secret-files = config.fudo.secrets.files.service-secrets.procul;
in { # in {
enable = true; # enable = true;
jabber-user = "niten@jabber.fudo.org"; # jabber-user = "niten@jabber.fudo.org";
staging = { # staging = {
secret-file = secret-files."chute-staging.secret"; # secret-file = secret-files."chute-staging.secret";
passphrase-file = secret-files."chute-staging.passphrase"; # passphrase-file = secret-files."chute-staging.passphrase";
key-file = secret-files."chute-staging.key"; # key-file = secret-files."chute-staging.key";
}; # };
}; # };
}; };
fudo = { fudo = {
@ -112,31 +119,31 @@ in {
}; };
}; };
"imap.${domain-name}" = { # "imap.${domain-name}" = {
admin-email = "admin@${domain-name}"; # admin-email = "admin@${domain-name}";
local-copies.dovecot = { # local-copies.dovecot = {
user = config.services.dovecot2.user; # user = config.services.dovecot2.user;
dependent-services = [ "dovecot2.service" ]; # dependent-services = [ "dovecot2.service" ];
}; # };
}; # };
"smtp.${domain-name}" = { # "smtp.${domain-name}" = {
admin-email = "admin@${domain-name}"; # admin-email = "admin@${domain-name}";
local-copies.postfix = { # local-copies.postfix = {
user = config.services.postfix.user; # user = config.services.postfix.user;
dependent-services = [ "postfix.service" ]; # dependent-services = [ "postfix.service" ];
}; # };
}; # };
}; };
secrets.host-secrets.procul = let secrets.host-secrets.procul = let
files = config.fudo.secrets.files; files = config.fudo.secrets.files;
in { in {
postgres-keytab = { # postgres-keytab = {
source-file = files.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 = files.service-passwords.procul.gitea-database; source-file = files.service-passwords.procul.gitea-database;
@ -144,6 +151,12 @@ in {
user = config.fudo.git.user; user = config.fudo.git.user;
}; };
postgres-gitea-password = {
source-file = files.service-passwords.procul.gitea-database;
target-file = "/srv/postgres-users/database.passwd";
user = config.services.postgresql.superUser;
};
heimdal-master-key = { heimdal-master-key = {
source-file = files.realm-master-keys."INFORMIS.LAND"; source-file = files.realm-master-keys."INFORMIS.LAND";
target-file = "/run/heimdal/master-key"; target-file = "/run/heimdal/master-key";
@ -155,6 +168,18 @@ in {
target-file = "/run/chute/staging/credentials.env"; target-file = "/run/chute/staging/credentials.env";
user = "root"; user = "root";
}; };
grafana-postgres-password = {
source-file = grafana-database-password;
target-file = "/run/metrics/grafana/db.passwd";
user = config.systemd.services.grafana.serviceConfig.User;
};
postgres-grafana-password = {
source-file = grafana-database-password;
target-file = "/run/postgres-users/grafana.passwd";
user = config.services.postgresql.superUser;
};
}; };
client.dns = { client.dns = {
@ -164,15 +189,6 @@ in {
external-interface = "extif0"; external-interface = "extif0";
}; };
services = {
auth = {
kerberos = {
state-directory = "/var/lib/kerberos";
master-key-file = host-secrets.heimdal-master-key.target-file;
};
};
};
secure-dns-proxy = { secure-dns-proxy = {
enable = true; enable = true;
upstream-dns = upstream-dns =
@ -183,67 +199,112 @@ in {
allowed-networks = [ "1.1.1.1/32" "1.0.0.1/32" "localhost" "link-local" ]; allowed-networks = [ "1.1.1.1/32" "1.0.0.1/32" "localhost" "link-local" ];
}; };
mail-server = { services.mail-server = {
state-directory = "/srv/mailserver";
};
# mail-server = {
# enable = true;
# debug = true;
# domain = domain-name;
# mail-hostname = "${host-fqdn}";
# monitoring = false;
# mail-user = "mailuser";
# mail-user-id = 525;
# mail-group = "mailgroup";
# clamav.enable = true;
# dkim.signing = true;
# dovecot = let
# cert-copy =
# host-certs."imap.${domain-name}".local-copies.dovecot;
# in {
# ssl-certificate = cert-copy.full-certificate;
# ssl-private-key = cert-copy.private-key;
# };
# postfix = let
# cert-copy =
# host-certs."smtp.${domain-name}".local-copies.postfix;
# in {
# ssl-certificate = cert-copy.full-certificate;
# ssl-private-key = cert-copy.private-key;
# };
# # This should NOT include the primary domain
# local-domains = [ host-fqdn "smtp.${domain-name}" ];
# mail-directory = "/srv/mailserver/mail";
# state-directory = "/srv/mailserver/state";
# trusted-networks = [ "172.86.179.16/29" "127.0.0.0/16" ];
# alias-users = {
# root = [ "niten" ];
# postmaster = [ "niten" ];
# hostmaster = [ "niten" ];
# webmaster = [ "niten" ];
# system = [ "niten" ];
# admin = [ "niten" ];
# dmarc-report = [ "niten" ];
# };
# };
services = {
auth = {
kerberos = {
state-directory = "/var/lib/kerberos";
master-key-file = host-secrets.heimdal-master-key.target-file;
};
};
dns.zones."informis.land" = {
enable = true; enable = true;
debug = true; default-host = host-ipv4;
};
domain = domain-name; postgresql = {
mail-hostname = "${host-fqdn}"; state-directory = "/state/services/postgresql";
monitoring = false; keytab = config.fudo.secrets.files.service-keytabs.procul.postgres;
mail-user = "mailuser"; };
mail-user-id = 525; logging.loki.state-directory = "/state/services/loki";
mail-group = "mailgroup"; metrics = {
clamav.enable = true; prometheus.state-directory = "/state/services/prometheus";
dkim.signing = true; grafana = {
state-directory = "/state/services/grafana";
dovecot = let database = {
cert-copy = user = "grafana";
host-certs."imap.${domain-name}".local-copies.dovecot; password-file = host-secrets.grafana-postgres-password.target-file;
in {
ssl-certificate = cert-copy.full-certificate;
ssl-private-key = cert-copy.private-key;
}; };
postfix = let
cert-copy =
host-certs."smtp.${domain-name}".local-copies.postfix;
in {
ssl-certificate = cert-copy.full-certificate;
ssl-private-key = cert-copy.private-key;
}; };
# This should NOT include the primary domain
local-domains = [ host-fqdn "smtp.${domain-name}" ];
mail-directory = "/srv/mailserver/mail";
state-directory = "/srv/mailserver/state";
trusted-networks = [ "172.86.179.16/29" "127.0.0.0/16" ];
alias-users = {
root = [ "niten" ];
postmaster = [ "niten" ];
hostmaster = [ "niten" ];
webmaster = [ "niten" ];
system = [ "niten" ];
admin = [ "niten" ];
dmarc-report = [ "niten" ];
}; };
}; };
postgresql = let postgresql = let
cert-copy = host-certs.${host-fqdn}.local-copies.postgresql; # cert-copy = host-certs.${host-fqdn}.local-copies.postgresql;
in { in {
enable = true; # enable = true;
ssl-certificate = cert-copy.full-certificate; # ssl-certificate = cert-copy.full-certificate;
ssl-private-key = cert-copy.private-key; # ssl-private-key = cert-copy.private-key;
keytab = host-secrets.postgres-keytab.target-file; # keytab = host-secrets.postgres-keytab.target-file;
local-networks = local-networks; # local-networks = local-networks;
users = { users = {
grafana = {
password-file = host-secrets.postgres-grafana-password.target-file;
databases.grafana = {
access = "CONNECT";
entity-access = {
"ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
# "SELECT,INSERT,UPDATE,DELETE";
"ALL SEQUENCES IN SCHEMA public" = "ALL PRIVILEGES";
# "SELECT, UPDATE";
};
};
};
gituser = { gituser = {
password-file = password-file =
host-secrets.gitea-database-password.target-file; host-secrets.postgres-gitea-password.target-file;
databases = { databases = {
git = { git = {
access = "CONNECT"; access = "CONNECT";
@ -256,7 +317,10 @@ in {
}; };
}; };
databases = { git = { users = [ "niten" ]; }; }; databases = {
grafana.users = config.instance.local-admins;
git.users = config.instance.local-admins;
};
}; };
git = { git = {

View File

@ -35,6 +35,7 @@ in {
environment = { environment = {
systemPackages = with pkgs; [ systemPackages = with pkgs; [
nixopsUnstable nixopsUnstable
openssl
]; ];
etc = { etc = {

View File

@ -7,6 +7,7 @@
interfaces = { interfaces = {
intif0.useDHCP = true; intif0.useDHCP = true;
}; };
firewall.enable = false;
}; };
i18n.inputMethod = { i18n.inputMethod = {
@ -16,4 +17,12 @@
fcitx5-rime fcitx5-rime
]; ];
}; };
# fudo.adguard-dns-proxy = {
# enable = true;
# http.listen-ip = "10.0.0.108";
# dns.listen-port = 1053;
# local-domain-name = "sea.fudo.org";
# verbose = true;
# };
} }

View File

@ -19,7 +19,9 @@ in {
"L /var/lib/flatpak - - - - ${state-dir}/lib/flatpak" "L /var/lib/flatpak - - - - ${state-dir}/lib/flatpak"
]; ];
services.openssh.hostKeys = [ services = {
blueman.enable = true;
openssh.hostKeys = [
{ {
path = "${state-dir}/ssh/ssh_host_rsa_key"; path = "${state-dir}/ssh/ssh_host_rsa_key";
type = "rsa"; type = "rsa";
@ -31,6 +33,7 @@ in {
bits = 4096; bits = 4096;
} }
]; ];
};
environment.etc = { environment.etc = {
"ssh/ssh_host_rsa_key" = { "ssh/ssh_host_rsa_key" = {
@ -63,8 +66,13 @@ in {
NIXOS.source = "${state-dir}/host/NIXOS"; NIXOS.source = "${state-dir}/host/NIXOS";
}; };
hardware.bluetooth.enable = true; hardware = {
hardware.xpadneo.enable = true; bluetooth = {
enable = true;
package = pkgs.bluezFull;
};
xpadneo.enable = true;
};
security.sudo.extraConfig = '' security.sudo.extraConfig = ''
# Due to rollback, sudo will lecture after every reboot # Due to rollback, sudo will lecture after every reboot

View File

@ -6,6 +6,7 @@ with lib;
local-host = config.instance.hostname; local-host = config.instance.hostname;
local-domain = config.fudo.hosts.${local-host}.domain; local-domain = config.fudo.hosts.${local-host}.domain;
local-site = config.fudo.hosts.${local-host}.site; local-site = config.fudo.hosts.${local-host}.site;
local-zone = config.fudo.domains.${local-domain}.zone;
host = config.fudo.hosts.${local-host}; host = config.fudo.hosts.${local-host};
@ -55,7 +56,8 @@ with lib;
local-groups local-groups
local-hosts local-hosts
local-profile local-profile
local-networks; local-networks
local-zone;
}; };
}; };
} }

View File

@ -4,6 +4,7 @@ with lib;
let let
hostname = config.instance.hostname; hostname = config.instance.hostname;
domain = config.instance.local-domain; domain = config.instance.local-domain;
zone-name = domain.zone;
cfg = config.fudo.domains.${domain}; cfg = config.fudo.domains.${domain};
in { in {
@ -38,8 +39,7 @@ in {
}; };
}; };
dns.domains.${domain} = { zones.${zone-name} = {
zone-definition = mkIf kerberized-domain {
srv-records = let srv-records = let
get-fqdn = hostname: get-fqdn = hostname:
"${hostname}.${config.fudo.hosts.${hostname}.domain}"; "${hostname}.${config.fudo.hosts.${hostname}.domain}";
@ -69,5 +69,4 @@ in {
}; };
}; };
}; };
};
} }

View File

@ -2,6 +2,8 @@
with lib; with lib;
let let
hostname = config.instance.hostname;
# Available to all users on the system. Keep it minimal. # Available to all users on the system. Keep it minimal.
global-packages = with pkgs; [ global-packages = with pkgs; [
bind bind
@ -15,11 +17,7 @@ let
wget wget
]; ];
import-paths = [ import-paths = [ ./build ./host ./user ];
./build
./host
./user
];
in { in {
@ -27,19 +25,13 @@ in {
is-regular-file = filename: type: type == "regular" || type == "link"; is-regular-file = filename: type: type == "regular" || type == "link";
regular-files = path: regular-files = path:
attrNames (filterAttrs is-regular-file (builtins.readDir path)); attrNames (filterAttrs is-regular-file (builtins.readDir path));
is-nix-file = filename: (builtins.match "^(.+)\.nix$" filename) != null; is-nix-file = filename: (builtins.match "^(.+).nix$" filename) != null;
nix-files = path: nix-files = path:
map map (file: path + "/${file}") (filter is-nix-file (regular-files path));
(file: path + "/${file}")
(filter is-nix-file (regular-files path));
in concatMap nix-files import-paths; in concatMap nix-files import-paths;
config = { config = {
environment = { fudo.hosts.${hostname}.local-networks = [ "::1/128" ];
etc.nixos-live.source = ../../.;
systemPackages = global-packages;
};
system.autoUpgrade.enable = false; system.autoUpgrade.enable = false;
@ -51,7 +43,7 @@ in {
}; };
nixpkgs.config.allowUnfree = true; nixpkgs.config.allowUnfree = true;
security.acme.acceptTerms = true;
hardware.enableRedistributableFirmware = true; hardware.enableRedistributableFirmware = true;
krb5 = { krb5 = {
@ -89,8 +81,8 @@ in {
''; '';
}; };
fail2ban = let fail2ban =
domain-name = config.fudo.hosts.${config.instance.hostname}.domain; let domain-name = config.fudo.hosts.${config.instance.hostname}.domain;
in { in {
enable = config.networking.firewall.enable; enable = config.networking.firewall.enable;
bantime-increment.enable = true; bantime-increment.enable = true;
@ -102,6 +94,11 @@ in {
xkbOptions = "ctrl:nocaps"; xkbOptions = "ctrl:nocaps";
}; };
btrfs.autoScrub.enable = let
btrfsFilesystems = filter (fsOpts: fsOpts.fsType == "btrfs")
(attrValues config.fileSystems);
in length btrfsFilesystems > 0;
# pcscd.enable = true; # pcscd.enable = true;
# udev.packages = with pkgs; [ yubikey-personalization ]; # udev.packages = with pkgs; [ yubikey-personalization ];
}; };
@ -143,7 +140,14 @@ in {
}; };
}; };
security.pam = { security = {
acme.acceptTerms = true;
sudo.extraConfig = ''
# rollback results in sudo lectures after each reboot
Defaults lecture = never
'';
pam = {
enableSSHAgentAuth = true; enableSSHAgentAuth = true;
services = { services = {
@ -155,9 +159,8 @@ in {
}; };
}; };
}; };
home-manager = {
useGlobalPkgs = true;
}; };
home-manager = { useGlobalPkgs = true; };
}; };
} }

View File

@ -4,7 +4,7 @@ with lib;
let let
list-contains = lst: item: any (i: i == item) lst; list-contains = lst: item: any (i: i == item) lst;
domain-realm = domain: domainOpts: domainOpts.gssapi-realm; domain-realm = _: domainOpts: domainOpts.gssapi-realm;
user-realms = username: user-realms = username:
mapAttrsToList domain-realm mapAttrsToList domain-realm

View File

@ -1,59 +1,217 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... } @ toplevel:
with lib; with lib;
let let
hostname = config.instance.hostname; hostname = config.instance.hostname;
domain-name = config.instance.local-domain;
cfg = config.fudo.services.dns;
nameserverOpts = { name, ... }: {
options = with types; {
hostname = mkOption {
type = str;
description = "Hostname of the external nameserver.";
default = name;
};
ipv4-address = mkOption {
type = nullOr str;
description = "Host ipv4 address of the external nameserver.";
default = null;
};
ipv6-address = mkOption {
type = nullOr str;
description = "Host ipv6 address of the external nameserver.";
default = null;
};
authoritative-hostname = mkOption {
type = nullOr str;
description = "Authoritative hostname of this nameserver.";
default = null;
};
description = mkOption {
type = str;
description = "Description of the external nameserver.";
};
};
};
zoneOpts = { name, ... }: let
zone-name = name;
in {
options = with types; {
enable = mkOption {
type = bool;
description = "Enable ${zone-name} zone on the local nameserver.";
default = zone-name == toplevel.config.instance.local-zone;
};
default-host = mkOption {
type = nullOr str;
description = "IP which will respond to requests for the base domain.";
default = null;
};
external-nameservers = mkOption {
type = listOf (submodule nameserverOpts);
description = "Off-network secondary nameservers.";
default = [];
};
domain = mkOption {
type = str;
description = "Domain which this zone serves.";
default = zone-name;
};
};
};
pthru = obj:
builtins.trace "TRACE: ${ obj }" obj;
in {
options.fudo.services.dns = with types; {
zones = mkOption {
type = attrsOf (submodule zoneOpts);
description = "Map of served zone to extra zone details.";
default = {};
};
};
config.fudo = {
zones = mapAttrs (zone-name: zone-cfg: let
domain-name = zone-cfg.domain;
domain = config.fudo.domains.${domain-name}; domain = config.fudo.domains.${domain-name};
make-srv-record = port: host: {
inherit port host;
};
served-domain = domain.primary-nameserver != null; served-domain = domain.primary-nameserver != null;
is-primary-nameserver = hostname == domain.primary-nameserver;
primary-nameserver = domain.primary-nameserver; primary-nameserver = domain.primary-nameserver;
primary-nameserver-ip = pkgs.lib.network.host-ipv4 config primary-nameserver;
is-primary-nameserver = hostname == primary-nameserver;
internal-nameserver-hostnames =
[domain.primary-nameserver] ++ domain.secondary-nameservers;
get-host-deets = description: hostname: {
ipv4-address = pkgs.lib.network.host-ipv4 config hostname;
ipv6-address = pkgs.lib.network.host-ipv6 config hostname;
description = description;
};
get-ns-deets = hostname: let
host-domain = config.fudo.hosts.${hostname}.domain;
desc = "${domain-name} nameserver ${hostname}.${host-domain}.";
in get-host-deets desc hostname;
nameserver-deets = let
internal-nameservers = map get-ns-deets internal-nameserver-hostnames;
in internal-nameservers ++ zone-cfg.external-nameservers;
has-auth-hostname = ns-host: ns-opts:
(hasAttr "authoritative-hostname" ns-opts) &&
(ns-opts.authoritative-hostname != null);
all-nameservers = listToAttrs
(imap1
(i: nsOpts:
nameValuePair "ns${toString i}" nsOpts)
nameserver-deets);
nameserver-aliases =
mapAttrs (hostname: opts: "${opts.authoritative-hostname}.")
(filterAttrs has-auth-hostname all-nameservers);
nameserver-hosts = mapAttrs (hostname: opts: {
inherit (opts) ipv4-address ipv6-address description;
}) (filterAttrs (hostname: opts: ! has-auth-hostname hostname opts)
all-nameservers);
dns-srv-records = let
nameserver-srv-records = mapAttrsToList
(hostname: hostOpts: let
target-host = if (has-auth-hostname hostname hostOpts) then
"${hostOpts.authoritative-hostname}" else
"${hostname}.${domain-name}";
in make-srv-record 53 target-host)
all-nameservers;
in {
tcp.domain = nameserver-srv-records;
udp.domain = nameserver-srv-records;
};
# TODO: move this to a mail service
mail-srv-records = optionalAttrs (domain.primary-mailserver != null) {
tcp = let
mailserver-domain = config.fudo.hosts.${domain.primary-mailserver}.domain;
fqdn = "mail.${mailserver-domain}";
in {
smtp = [(make-srv-record 25 fqdn)];
submission = [(make-srv-record 587 fqdn)];
imap = [(make-srv-record 143 fqdn)];
imaps = [(make-srv-record 993 fqdn)];
pop3 = [(make-srv-record 110 fqdn)];
pop3s = [(make-srv-record 995 fqdn)];
};
};
in { in {
config = mkIf (served-domain) { gssapi-realm = domain.gssapi-realm;
fudo.dns = {
hosts = nameserver-hosts // {
mail = mkIf (domain.primary-nameserver != null) (let
mailserver-deets = host: let
host-domain = config.fudo.hosts.${host}.domain;
in get-host-deets "Primary ${domain-name} mailserver ${host}.${host-domain}." host;
in mailserver-deets domain.primary-nameserver);
};
aliases = nameserver-aliases;
mx = optional (domain.primary-mailserver != null)
(let
mail-domain-name = config.fudo.hosts.${domain.primary-mailserver}.domain;
in "mail.${mail-domain-name}");
dmarc-report-address = "dmarc-report@${domain-name}";
nameservers = let
direct-external = attrValues nameserver-aliases;
internal = map (hostname: "${hostname}.${domain-name}.")
(attrNames nameserver-hosts);
in internal ++ direct-external;
srv-records = dns-srv-records // mail-srv-records;
}) cfg.zones;
dns = let
domain-name = config.instance.local-domain;
domain = config.fudo.domains.${domain-name};
primary-nameserver = domain.primary-nameserver;
primary-nameserver-ip = pkgs.lib.network.host-ipv4 config primary-nameserver;
primary-nameserver-fqdn = "${primary-nameserver}.${domain-name}";
is-primary-nameserver = primary-nameserver == hostname;
in {
enable = is-primary-nameserver; enable = is-primary-nameserver;
identity = "${hostname}.${domain-name}."; identity = "${hostname}.${domain-name}";
nameservers = {
ns1 = {
ipv4-address = primary-nameserver-ip;
description = "Primary ${domain-name} nameserver";
};
};
listen-ips = optionals is-primary-nameserver listen-ips = optionals is-primary-nameserver
(pkgs.lib.network.host-ips config hostname); (pkgs.lib.network.host-ips config hostname);
domains = { domains = mapAttrs' (zone-name: zone-cfg:
${domain-name} = { nameValuePair zone-cfg.domain
{
dnssec = true; dnssec = true;
default-host = primary-nameserver-ip; zone-definition = config.fudo.zones.${zone-name};
gssapi-realm = domain.gssapi-realm; }) cfg.zones;
mx = optional (domain.primary-mailserver != null)
domain.primary-mailserver;
dmarc-report-address = "dmarc-report@${domain-name}";
zone-definition = let
zone = config.fudo.zones.${domain-name};
make-dns-srv-record = hostname: {
port = 53;
host = hostname;
};
in zone // {
srv-records = {
tcp.domain = map make-dns-srv-record [ "ns1.${domain-name}" ];
udp.domain = map make-dns-srv-record [ "ns1.${domain-name}" ];
};
};
};
};
}; };
}; };
} }

View File

@ -6,6 +6,8 @@ let
domain-name = config.fudo.services.auth.domain; domain-name = config.fudo.services.auth.domain;
domain = config.fudo.domains.${domain-name}; domain = config.fudo.domains.${domain-name};
zone-name = domain.zone;
ldap-server = elem hostname domain.ldap-servers; ldap-server = elem hostname domain.ldap-servers;
kerberos-master = hostname == domain.kerberos-master; kerberos-master = hostname == domain.kerberos-master;
@ -62,7 +64,8 @@ in {
}; };
}; };
config.fudo = { config = {
fudo = {
acme.host-domains.${hostname} = mkIf (ldap-server) { acme.host-domains.${hostname} = mkIf (ldap-server) {
${cfg.ldap.hostname}.local-copies.openldap = { ${cfg.ldap.hostname}.local-copies.openldap = {
user = config.services.openldap.user; user = config.services.openldap.user;
@ -120,7 +123,7 @@ in {
}; };
}; };
dns.domains.${domain-name} = let zones.${zone-name} = let
make-srv-record = port: hostname: { make-srv-record = port: hostname: {
port = port; port = port;
host = hostname; host = hostname;
@ -129,17 +132,17 @@ in {
get-fqdn = host: get-fqdn = host:
"${host}.${config.fudo.hosts.${host}.domain}"; "${host}.${config.fudo.hosts.${host}.domain}";
kerberos-masters = optional (kerberized-domain) kerberos-master-hosts = optional (kerberized-domain)
domain.kerberos-master; domain.kerberos-master;
kerberos-servers = map get-fqdn kerberos-servers = map get-fqdn
(kerberos-masters ++ domain.kerberos-slaves); (kerberos-master-hosts ++ domain.kerberos-slaves);
master-servers = map get-fqdn kerberos-masters; kerberos-masters = map get-fqdn kerberos-master-hosts;
ldap-servers = map get-fqdn domain.ldap-servers; ldap-servers = map get-fqdn domain.ldap-servers;
in { in {
zone-definition.srv-records = { srv-records = {
tcp = { tcp = {
kerberos = map (make-srv-record 88) kerberos-servers; kerberos = map (make-srv-record 88) kerberos-servers;
kerberos-adm = map (make-srv-record 749) kerberos-masters; kerberos-adm = map (make-srv-record 749) kerberos-masters;
@ -155,4 +158,5 @@ in {
}; };
}; };
}; };
};
} }

View File

@ -3,13 +3,16 @@
with lib; with lib;
let let
cfg = config.fudo.services.jabber; cfg = config.fudo.services.jabber;
domain-name = config.instance.local-domain;
domain = config.fudo.domains.${domain-name};
zone-name = domain.zone;
hostname = config.instance.hostname; hostname = config.instance.hostname;
host-secrets = config.fudo.secrets.host-secrets.${hostname}; host-secrets = config.fudo.secrets.host-secrets.${hostname};
xmpp-server = elem hostname domain.xmpp-servers;
in { in {
options.fudo.services.jabber = with types; { options.fudo.services.jabber = with types; {
enable = mkEnableOption "Enable Jabber server on this host.";
user = mkOption { user = mkOption {
type = str; type = str;
description = "User as which to run the ejabberd server."; description = "User as which to run the ejabberd server.";
@ -22,11 +25,13 @@ in {
default = "ejabberd"; default = "ejabberd";
}; };
hostname = mkOption { # hostname = mkOption {
type = str; # type = str;
description = "Hostname of the user jabber server."; # description = "Hostname of the user jabber server.";
default = "jabber.fudo.org"; # default = "jabber.${domain-name}";
}; # };
enable-pubsub = mkEnableOption "Enable PubSub module.";
domain = mkOption { domain = mkOption {
type = str; type = str;
@ -57,6 +62,10 @@ in {
ejabberd-ldap-auth-passwd-file = ejabberd-ldap-auth-passwd-file =
pkgs.lib.passwd.stablerandom-passwd-file "ejabberd-auth-passwd-file" pkgs.lib.passwd.stablerandom-passwd-file "ejabberd-auth-passwd-file"
"ejabberd-auth-passwd-file-${config.instance.build-seed}"; "ejabberd-auth-passwd-file-${config.instance.build-seed}";
hostname-map = listToAttrs
(imap0 (i: hostname: nameValuePair hostname "xmpp-${toString i}")
domain.xmpp-servers);
in { in {
system-users.${cfg.ldap.user} = { system-users.${cfg.ldap.user} = {
description = "ejabberd authentication user."; description = "ejabberd authentication user.";
@ -65,7 +74,25 @@ in {
ejabberd-ldap-auth-passwd-file; ejabberd-ldap-auth-passwd-file;
}; };
jabber = mkIf cfg.enable { zones.${zone-name} = {
srv-records.tcp = let
to-fqdn = server: "${hostname-map.${server}}.${domain-name}";
in {
xmpp-server = map (host: {
host = to-fqdn host;
port = 5269;
}) domain.xmpp-servers;
xmpp-client = map (host: {
host = to-fqdn host;
port = 5222;
}) domain.xmpp-servers;
};
aliases = mapAttrs' (host: alias: nameValuePair alias host)
hostname-map;
};
jabber = mkIf xmpp-server {
enable = true; enable = true;
state-directory = cfg.state-directory; state-directory = cfg.state-directory;
@ -76,7 +103,7 @@ in {
sites = { sites = {
${cfg.domain} = { ${cfg.domain} = {
hostname = cfg.hostname; hostname = "${hostname-map.${hostname}}.${domain-name}";
site-config = { site-config = {
auth_method = "ldap"; auth_method = "ldap";
ldap_servers = cfg.ldap.servers; ldap_servers = cfg.ldap.servers;

View File

@ -1,5 +1,6 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
with lib;
let local-domain = "sea.fudo.org"; let local-domain = "sea.fudo.org";
in { in {
fileSystems = { fileSystems = {
@ -15,12 +16,12 @@ in {
# }; # };
"/mnt/music" = { "/mnt/music" = {
device = "doraemon.${local-domain}:/volume1/Music"; device = "doraemon.${local-domain}:/volume1/Music";
fsType = "nfs4"; fsType = "nfs";
options = [ "comment=systemd.automount" ]; options = [ "comment=systemd.automount" ];
}; };
"/mnt/video" = { "/mnt/video" = {
device = "doraemon.${local-domain}:/volume1/Video"; device = "doraemon.${local-domain}:/volume1/Video";
fsType = "nfs4"; fsType = "nfs";
options = [ "comment=systemd.automount" ]; options = [ "comment=systemd.automount" ];
}; };
# fileSystems."/mnt/security" = { # fileSystems."/mnt/security" = {
@ -29,67 +30,135 @@ in {
# }; # };
"/mnt/cargo_video" = { "/mnt/cargo_video" = {
device = "cargo.${local-domain}:/volume1/video"; device = "cargo.${local-domain}:/volume1/video";
fsType = "nfs4"; fsType = "nfs";
options = [ "comment=systemd.automount" ]; options = [ "comment=systemd.automount" "nfsvers=4.2" ];
}; };
"/mnt/photo" = { "/mnt/photo" = {
device = "cargo.${local-domain}:/volume1/pictures"; device = "cargo.${local-domain}:/volume1/pictures";
fsType = "nfs4"; fsType = "nfs";
options = [ "comment=systemd.automount" ]; options = [ "comment=systemd.automount" "nfsvers=4.2" ];
}; };
# "proto=tcp" # "proto=tcp"
# NOTE: these are pointing directly to nostromo so the krb lookup works # # NOTE: these are pointing directly to nostromo so the krb lookup works
"/net/documents" = { "/net/documents" = {
device = "nostromo.sea.fudo.org:/export/documents"; device = "nostromo.sea.fudo.org:/export/documents";
fsType = "nfs4"; fsType = "nfs4";
options = [ "comment=systemd.automount" "sec=krb5p" "nfsvers=4" ]; options = [
"sec=krb5p"
"x-systemd.automount"
# "vers=4"
# "minorversion=2"
# "proto=tcp"
];
}; };
"/net/downloads" = { "/net/downloads" = {
device = "nostromo.sea.fudo.org:/export/downloads"; device = "nostromo.sea.fudo.org:/export/downloads";
fsType = "nfs4"; fsType = "nfs4";
options = [ "comment=systemd.automount" "sec=krb5i" "nfsvers=4" ]; options = [
"sec=krb5i"
"x-systemd.automount"
# "vers=4"
# "minorversion=2"
# "proto=tcp"
];
}; };
"/net/projects" = { "/net/projects" = {
device = "nostromo.sea.fudo.org:/export/projects"; device = "nostromo.sea.fudo.org:/export/projects";
fsType = "nfs4"; fsType = "nfs4";
options = [ "comment=systemd.automount" "sec=krb5p" "nfsvers=4" ]; options = [
"sec=krb5p"
"x-systemd.automount"
# "vers=4"
# "minorversion=2"
# "proto=tcp"
];
}; };
}; };
systemd = { systemd = {
## This fails if the filesystems already exist # paths.host-keytab-modified = {
# tmpfiles.rules = [ # wantedBy = [ "multi-user.target" ];
# "d /net/documents - root sea-documents - -" # pathConfig = {
# "d /net/downloads - root sea-downloads - -" # PathChanged = "/etc/krb5.keytab";
# "d /net/projects - root sea-projects - -" # Unit = "host-keytab-modified.service";
# ]; # };
# };
# mounts = [ # services.host-keytab-modified = {
# description = "Operations to execute when keytab is changed.";
# script = "${pkgs.systemd}/bin/systemctl restart rpc-gssd.service";
# };
services.host-keytab-watcher = {
wantedBy = [ "rpc-gssd.service" "rpc-svcgssd.service" ];
before = [ "rpc-gssd.service" "rpc-svcgssd.service" ];
unitConfig = { ConditionPathExists = [ "/etc/krb5.keytab" ]; };
serviceConfig = {
ExecStart = "${pkgs.coreutils}/bin/sleep 500";
TimeoutStartSec = "3600";
RemainAfterExit = true;
};
};
};
# systemd = {
# ## 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 = let
# mkOpts =
# concatStringsSep ",";
# in [
# { # {
# enable = true;
# what = "nostromo.sea.fudo.org:/export/documents"; # what = "nostromo.sea.fudo.org:/export/documents";
# where = "/net/documents"; # where = "/net/documents";
# type = "nfs4"; # type = "nfs";
# options = "sec=krb5p"; # options = mkOpts [
# "vers=4"
# "minorversion=2"
# "sec=krb5p"
# "x-systemd.automount"
# "proto=tcp"
# ];
# description = "sea-store documents on encrypted filesysem."; # description = "sea-store documents on encrypted filesysem.";
# } # }
# { # {
# enable = true;
# what = "nostromo.sea.fudo.org:/export/downloads"; # what = "nostromo.sea.fudo.org:/export/downloads";
# where = "/net/downloads"; # where = "/net/downloads";
# type = "nfs4"; # type = "nfs";
# options = "sec=krb5i"; # options = mkOpts [
# "vers=4"
# "minorversion=2"
# "sec=krb5i"
# "x-systemd.automount"
# "proto=tcp"
# ];
# description = "sea-store downloads on encrypted filesysem."; # description = "sea-store downloads on encrypted filesysem.";
# } # }
# { # {
# enable = true;
# what = "nostromo.sea.fudo.org:/export/projects"; # what = "nostromo.sea.fudo.org:/export/projects";
# where = "/net/projects"; # where = "/net/projects";
# type = "nfs4"; # type = "nfs";
# options = "sec=krb5p"; # options = mkOpts [
# "vers=4"
# "minorversion=2"
# "sec=krb5p"
# "x-systemd.automount"
# "proto=tcp"
# ];
# description = "sea-store projects on encrypted filesysem."; # description = "sea-store projects on encrypted filesysem.";
# } # }
# ]; # ];
}; # };
services.printing = { services.printing = {
enable = true; enable = true;

View File

@ -338,9 +338,7 @@
primary-group = "selby"; primary-group = "selby";
common-name = "Vee Selby"; common-name = "Vee Selby";
ldap-hashed-passwd = "snoinuer"; ldap-hashed-passwd = "snoinuer";
email-aliases = [ email-aliases = [ "virginia@selby.ca" ];
"virginia@selby.ca"
];
}; };
dabar = { dabar = {

View File

@ -136,15 +136,22 @@
"inputs": { "inputs": {
"doom-emacs": "doom-emacs_2", "doom-emacs": "doom-emacs_2",
"doom-snippets": "doom-snippets", "doom-snippets": "doom-snippets",
"emacs-overlay": "emacs-overlay", "emacs-overlay": [
"fudo-home",
"emacs-overlay"
],
"emacs-so-long": "emacs-so-long", "emacs-so-long": "emacs-so-long",
"evil-markdown": "evil-markdown", "evil-markdown": "evil-markdown",
"evil-org-mode": "evil-org-mode", "evil-org-mode": "evil-org-mode",
"evil-quick-diff": "evil-quick-diff", "evil-quick-diff": "evil-quick-diff",
"explain-pause-mode": "explain-pause-mode", "explain-pause-mode": "explain-pause-mode",
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
"format-all": "format-all",
"nix-straight": "nix-straight", "nix-straight": "nix-straight",
"nixpkgs": "nixpkgs_5", "nixpkgs": [
"fudo-home",
"nixpkgs"
],
"nose": "nose", "nose": "nose",
"ob-racket": "ob-racket", "ob-racket": "ob-racket",
"org": "org", "org": "org",
@ -155,15 +162,15 @@
"rotate-text": "rotate-text" "rotate-text": "rotate-text"
}, },
"locked": { "locked": {
"lastModified": 1627398156, "lastModified": 1645751511,
"narHash": "sha256-Ru1aV3NuIFXAsvUE3de8KR7xDZOo1GCBJdsWKJn+Ebw=", "narHash": "sha256-i3cMaHdaxwfeJEKVgk3Sxx/IRfjwNcThaCMcq4uv9jg=",
"owner": "vlaci", "owner": "nix-community",
"repo": "nix-doom-emacs", "repo": "nix-doom-emacs",
"rev": "fee14d217b7a911aad507679dafbeaa8c1ebf5ff", "rev": "ef434602f6f2a8b469d1b01f9edff4f5b6d7f555",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "vlaci", "owner": "nix-community",
"repo": "nix-doom-emacs", "repo": "nix-doom-emacs",
"type": "github" "type": "github"
} }
@ -171,16 +178,16 @@
"doom-emacs_2": { "doom-emacs_2": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1626604817, "lastModified": 1645634993,
"narHash": "sha256-z+dvjB02cHU+VQ5EMkzqSdX817PZar9AkmmfK27q0vo=", "narHash": "sha256-QeE6aUJxoaqHM28Cpt2rKC817VQvXGuuFUyLzehaC50=",
"owner": "hlissner", "owner": "hlissner",
"repo": "doom-emacs", "repo": "doom-emacs",
"rev": "46732c0adaef147144418f9f284ca6b1183ab96f", "rev": "42e5763782fdc1aabb9f2624d468248d6978abe2",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "hlissner", "owner": "hlissner",
"ref": "develop", "ref": "master",
"repo": "doom-emacs", "repo": "doom-emacs",
"type": "github" "type": "github"
} }
@ -188,11 +195,11 @@
"doom-snippets": { "doom-snippets": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1625547004, "lastModified": 1645652740,
"narHash": "sha256-V+ytAjB4ZZ+5dJJAu1OY7SbnqrokX5PVBWs0AsgQ8Vs=", "narHash": "sha256-ci5QsTkzmfSd7Pfoe+RActuSOmMY2TvJL7f2giCwNEI=",
"owner": "hlissner", "owner": "hlissner",
"repo": "doom-snippets", "repo": "doom-snippets",
"rev": "5c0eb5bd70f035cefb981c2ce64f4367498bdda6", "rev": "02aca23fef94fc7a58836fd1812d62e731249fa3",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -204,11 +211,11 @@
"emacs-overlay": { "emacs-overlay": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1626972035, "lastModified": 1645953123,
"narHash": "sha256-YhBtnKmLDYiEzP5ZEMEQMg6oMP5EV+ToCkku7ZYfL+A=", "narHash": "sha256-Be06ikbfQTuRwsU6nxNbMSvSUOzmGzDOLBKXFMekrcA=",
"owner": "nix-community", "owner": "nix-community",
"repo": "emacs-overlay", "repo": "emacs-overlay",
"rev": "be04b45efb35db58e6ac6aa86b84f850c85b5dfe", "rev": "058e38892484c1ab517c890b0aaee5d53565a494",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -343,11 +350,11 @@
}, },
"flake-utils": { "flake-utils": {
"locked": { "locked": {
"lastModified": 1623875721, "lastModified": 1644229661,
"narHash": "sha256-A8BU7bjS5GirpAUv4QA+QnJ4CceLHkcXdRp4xITDB0s=", "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "f7e004a55b120c02ecb6219596820fcd32ca8772", "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -356,6 +363,23 @@
"type": "github" "type": "github"
} }
}, },
"format-all": {
"flake": false,
"locked": {
"lastModified": 1581716637,
"narHash": "sha256-ul7LCe60W8TIvUmUtZtZRo8489TK9iTPDsLHmzxY57M=",
"owner": "lassik",
"repo": "emacs-format-all-the-code",
"rev": "47d862d40a088ca089c92cd393c6dca4628f87d3",
"type": "github"
},
"original": {
"owner": "lassik",
"repo": "emacs-format-all-the-code",
"rev": "47d862d40a088ca089c92cd393c6dca4628f87d3",
"type": "github"
}
},
"fudo-entities": { "fudo-entities": {
"inputs": { "inputs": {
"fudo-lib": "fudo-lib", "fudo-lib": "fudo-lib",
@ -364,11 +388,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1639927391, "lastModified": 1646155824,
"narHash": "sha256-fptxLDQu9dKzOz8XVtw/tGEsua1XHUF7pzYpzZ4igjU=", "narHash": "sha256-cVQ4mQNNblY2MjK4kaoW71wUccUOdczVt2Y3umGEkTw=",
"ref": "master", "ref": "master",
"rev": "e29d67c9ea522672e3fcf2e8d48edc61ba72ff0d", "rev": "4799d7704ae703693065c47e1e454e58f5e767f4",
"revCount": 25, "revCount": 76,
"type": "git", "type": "git",
"url": "https://git.fudo.org/fudo-nix/entities.git" "url": "https://git.fudo.org/fudo-nix/entities.git"
}, },
@ -380,17 +404,20 @@
"fudo-home": { "fudo-home": {
"inputs": { "inputs": {
"doom-emacs": "doom-emacs", "doom-emacs": "doom-emacs",
"emacs-overlay": "emacs-overlay",
"fudo-pkgs": "fudo-pkgs", "fudo-pkgs": "fudo-pkgs",
"home-manager": "home-manager", "home-manager": "home-manager",
"niten-doom-config": "niten-doom-config", "niten-doom-config": "niten-doom-config",
"nixpkgs": "nixpkgs_7" "nixpkgs": [
"nixpkgs"
]
}, },
"locked": { "locked": {
"lastModified": 1641413339, "lastModified": 1646777521,
"narHash": "sha256-31N7ovrwD6IagxFRGVDFNKq162h7s7wpBKAt+7bEhV0=", "narHash": "sha256-0WtNjhJ+66l+3l/s4bhqgIfsuROBtD4GJ0B3yJRipxM=",
"ref": "master", "ref": "master",
"rev": "3d18a37c6ef9815428d4a8babafa83cfbdafea57", "rev": "e860b7aee67d8f0dabcf95fdfde138722fca1f32",
"revCount": 75, "revCount": 124,
"type": "git", "type": "git",
"url": "https://git.fudo.org/fudo-nix/home.git" "url": "https://git.fudo.org/fudo-nix/home.git"
}, },
@ -401,11 +428,11 @@
}, },
"fudo-lib": { "fudo-lib": {
"locked": { "locked": {
"lastModified": 1638990149, "lastModified": 1641848738,
"narHash": "sha256-p1T0GMJXIJvTpVdn5nK7RZJX8izkabADJ/LsaL442zI=", "narHash": "sha256-9+xyFqyUIzIkNo2HyXxp6Lm9/f0EZqRqkRz52AQoW6Q=",
"ref": "master", "ref": "master",
"rev": "c87448ff1365c3d5230690f68d1ba246652581d1", "rev": "63b80fb5dc1e6ad69252a233b7c6a20f649884c6",
"revCount": 24, "revCount": 59,
"type": "git", "type": "git",
"url": "https://git.fudo.org/fudo-nix/lib.git" "url": "https://git.fudo.org/fudo-nix/lib.git"
}, },
@ -416,7 +443,7 @@
}, },
"fudo-lib_2": { "fudo-lib_2": {
"locked": { "locked": {
"narHash": "sha256-hlQ7nQYuIH7AfRu7O3yr8Xf8Ppqbf7lIU2QNQvnOAbU=", "narHash": "sha256-JWs8GEoZdR9sqf1nQJgIIQCwe4sQDZoK3C7WzQN3hAo=",
"path": "/state/fudo-lib", "path": "/state/fudo-lib",
"type": "path" "type": "path"
}, },
@ -426,15 +453,12 @@
} }
}, },
"fudo-pkgs": { "fudo-pkgs": {
"inputs": {
"unstableNixpkgs": "unstableNixpkgs"
},
"locked": { "locked": {
"lastModified": 1641413309, "lastModified": 1643841844,
"narHash": "sha256-FPLBuS9714BxkU6uLJSoRL7VQUj3yvTK4xkl7+RSzaM=", "narHash": "sha256-rmTIL94RQQaFhMHCopmeFUVAoP71nSA6sB46riDq2Ik=",
"ref": "master", "ref": "master",
"rev": "042aa2f4cea9ad8acdf93b4b54196aefd94c0408", "rev": "7e02ad0e7d9ac42605ed318e9d76364ec1d339ec",
"revCount": 22, "revCount": 41,
"type": "git", "type": "git",
"url": "https://git.fudo.org/fudo-nix/pkgs.git" "url": "https://git.fudo.org/fudo-nix/pkgs.git"
}, },
@ -444,15 +468,12 @@
} }
}, },
"fudo-pkgs_2": { "fudo-pkgs_2": {
"inputs": {
"unstableNixpkgs": "unstableNixpkgs_2"
},
"locked": { "locked": {
"lastModified": 1641413309, "lastModified": 1646862825,
"narHash": "sha256-FPLBuS9714BxkU6uLJSoRL7VQUj3yvTK4xkl7+RSzaM=", "narHash": "sha256-Zqtx4cJXuMG0dNKgmcJgfy3twLfRSMRqI/UMfl2hbsA=",
"ref": "master", "ref": "master",
"rev": "042aa2f4cea9ad8acdf93b4b54196aefd94c0408", "rev": "4ee3fb603b5b9d55c51213acbf90a52ce4c08cf1",
"revCount": 22, "revCount": 49,
"type": "git", "type": "git",
"url": "https://git.fudo.org/fudo-nix/pkgs.git" "url": "https://git.fudo.org/fudo-nix/pkgs.git"
}, },
@ -529,14 +550,17 @@
}, },
"home-manager": { "home-manager": {
"inputs": { "inputs": {
"nixpkgs": "nixpkgs_6" "nixpkgs": [
"fudo-home",
"nixpkgs"
]
}, },
"locked": { "locked": {
"lastModified": 1639871969, "lastModified": 1643933536,
"narHash": "sha256-6feWUnMygRzA9tzkrfAzpA5/NBYg75bkFxnqb1DtD7E=", "narHash": "sha256-yRmsWAG4DnLxLIUtlaZsl0kH7rN5xSoyNRlf0YZrcH4=",
"owner": "nix-community", "owner": "nix-community",
"repo": "home-manager", "repo": "home-manager",
"rev": "697cc8c68ed6a606296efbbe9614c32537078756", "rev": "2860d7e3bb350f18f7477858f3513f9798896831",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -577,16 +601,15 @@
"nix-straight": { "nix-straight": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1621543597, "lastModified": 1643475817,
"narHash": "sha256-E/m2Hrw2og//CfOCOWe2yapYC01Tqhozn4YMPYJsC3o=", "narHash": "sha256-NpExq5nbPbj/ppkBX3SnETEJuOne1MKJxen8vVHsDFg=",
"owner": "vlaci", "owner": "nix-community",
"repo": "nix-straight.el", "repo": "nix-straight.el",
"rev": "8e84d04f10b2298de856b2b8b9a0d13abc91b5ca", "rev": "08d75e5651cb52f8a07e03408ed19e04bee07505",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "vlaci", "owner": "nix-community",
"ref": "v2.2.0",
"repo": "nix-straight.el", "repo": "nix-straight.el",
"type": "github" "type": "github"
} }
@ -606,6 +629,21 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgsUnstable": {
"locked": {
"lastModified": 1647297614,
"narHash": "sha256-ulGq3W5XsrBMU/u5k9d4oPy65pQTkunR4HKKtTq0RwY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "73ad5f9e147c0d2a2061f1d4bd91e05078dc0b58",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-unstable",
"type": "indirect"
}
},
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1638196344, "lastModified": 1638196344,
@ -653,56 +691,11 @@
}, },
"nixpkgs_5": { "nixpkgs_5": {
"locked": { "locked": {
"lastModified": 1638407071, "lastModified": 1645296114,
"narHash": "sha256-xbveILjgtBVh6B5F6i2k3T0IrE8lZp1vsqfDY+Df/cg=", "narHash": "sha256-y53N7TyIkXsjMpOG7RhvqJFGDacLs9HlyHeSTBioqYU=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "971b383a28f4baee8ea3931af4840fa221929fd6", "rev": "530a53dcbc9437363471167a5e4762c5fcfa34a1",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixpkgs-unstable",
"type": "indirect"
}
},
"nixpkgs_6": {
"locked": {
"lastModified": 1638407071,
"narHash": "sha256-xbveILjgtBVh6B5F6i2k3T0IrE8lZp1vsqfDY+Df/cg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "971b383a28f4baee8ea3931af4840fa221929fd6",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_7": {
"locked": {
"lastModified": 1638407071,
"narHash": "sha256-xbveILjgtBVh6B5F6i2k3T0IrE8lZp1vsqfDY+Df/cg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "971b383a28f4baee8ea3931af4840fa221929fd6",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "971b383a28f4baee8ea3931af4840fa221929fd6",
"type": "github"
}
},
"nixpkgs_8": {
"locked": {
"lastModified": 1641229786,
"narHash": "sha256-WPPcLNbVu6ryj772GooUpF285LOvRHdOo/UNJgPnFYI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "88579effa7e88c25087faf6de6388d0cd1738dc0",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -746,11 +739,11 @@
"org": { "org": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1627155762, "lastModified": 1645557265,
"narHash": "sha256-XS1eA6P0ePabdrnUNe5lN19EA9dfK615gMGObr9wfBQ=", "narHash": "sha256-vBOWOOfdUbvpTkqs2Lx+OCPfUdZdzAOdGxzHBSAslmo=",
"owner": "emacs-straight", "owner": "emacs-straight",
"repo": "org-mode", "repo": "org-mode",
"rev": "c9dfed48a607c7f6524f1c6480f09cf61a5d6237", "rev": "282a01f22159b4855071ffd54a9ae6ce681c3690",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -762,11 +755,11 @@
"org-contrib": { "org-contrib": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1623339452, "lastModified": 1639727892,
"narHash": "sha256-E3pioqkmAKQm5N7YsgJZil0/ozkdRE7//tE9FGbrluM=", "narHash": "sha256-+T6Y87aSAx7kMpigm8d1ODDQIyPBM6a+4qGolXjCEXs=",
"ref": "master", "ref": "master",
"rev": "fc81309cf6756607a836f93049a9393c2967c4e0", "rev": "5766ff1088191e4df5fecd55007ba4271e609bcc",
"revCount": 2599, "revCount": 2611,
"type": "git", "type": "git",
"url": "https://git.sr.ht/~bzg/org-contrib" "url": "https://git.sr.ht/~bzg/org-contrib"
}, },
@ -822,11 +815,11 @@
"revealjs": { "revealjs": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1625811744, "lastModified": 1645450091,
"narHash": "sha256-Y67nVqcovn2PbHXmWOFWMq10Qz2ZIRyyWEO6qsZLbIM=", "narHash": "sha256-3fM1hKCbuIy8HzBv9JjjZW/RwE1CKeq++delBhbSvys=",
"owner": "hakimel", "owner": "hakimel",
"repo": "reveal.js", "repo": "reveal.js",
"rev": "b18f12d964ef80bd9ffb061aae48ff4c15fb43ad", "rev": "5e12c6aeb7a37acca7ca22c0bd29548f9ff282ea",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -844,7 +837,8 @@
"fudo-lib": "fudo-lib_2", "fudo-lib": "fudo-lib_2",
"fudo-pkgs": "fudo-pkgs_2", "fudo-pkgs": "fudo-pkgs_2",
"fudo-secrets": "fudo-secrets", "fudo-secrets": "fudo-secrets",
"nixpkgs": "nixpkgs_8" "nixpkgs": "nixpkgs_5",
"nixpkgsUnstable": "nixpkgsUnstable"
} }
}, },
"rotate-text": { "rotate-text": {
@ -911,36 +905,6 @@
"type": "path" "type": "path"
} }
}, },
"unstableNixpkgs": {
"locked": {
"lastModified": 1641230035,
"narHash": "sha256-hFyqihERaTbLxCOlugy/rpp22VLtLh8SPRnA2uu3F/8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "78cd22c1b8604de423546cd49bfe264b786eca13",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-unstable",
"type": "indirect"
}
},
"unstableNixpkgs_2": {
"locked": {
"lastModified": 1641230035,
"narHash": "sha256-hFyqihERaTbLxCOlugy/rpp22VLtLh8SPRnA2uu3F/8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "78cd22c1b8604de423546cd49bfe264b786eca13",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-unstable",
"type": "indirect"
}
},
"utils": { "utils": {
"locked": { "locked": {
"lastModified": 1637014545, "lastModified": 1637014545,

View File

@ -4,7 +4,10 @@
inputs = { inputs = {
nixpkgs.url = "nixpkgs/nixos-21.05"; nixpkgs.url = "nixpkgs/nixos-21.05";
fudo-home.url = "git+https://git.fudo.org/fudo-nix/home.git"; fudo-home = {
url = "git+https://git.fudo.org/fudo-nix/home.git";
inputs.nixpkgs.follows = "nixpkgs";
};
# This MUST be a clean git repo, because we use the timestamp. # This MUST be a clean git repo, because we use the timestamp.
fudo-entities = { fudo-entities = {
@ -25,6 +28,8 @@
chute.url = "git+https://git.fudo.org/chute/chute.git?ref=stable"; chute.url = "git+https://git.fudo.org/chute/chute.git?ref=stable";
chuteUnstable.url = "git+https://git.fudo.org/chute/chute.git?ref=master"; chuteUnstable.url = "git+https://git.fudo.org/chute/chute.git?ref=master";
nixpkgsUnstable.url = "nixpkgs/nixos-unstable";
}; };
outputs = { self, outputs = { self,
@ -36,6 +41,7 @@
fudo-secrets, fudo-secrets,
chute, chute,
chuteUnstable, chuteUnstable,
nixpkgsUnstable,
... } @ inputs: ... } @ inputs:
with nixpkgs.lib; with nixpkgs.lib;
let let
@ -45,7 +51,19 @@
fudo-networks = fudo-entities.entities.networks; fudo-networks = fudo-entities.entities.networks;
pkgs-for = arch: import nixpkgs { unstable-for = arch: import nixpkgsUnstable {
system = arch;
config = {
allowUnfree = true;
permittedInsecurePackages = [
"openssh-with-gssapi-8.4p1"
];
};
};
pkgs-for = arch: let
unstable = unstable-for arch;
in import nixpkgs {
system = arch; system = arch;
config = { config = {
allowUnfree = true; allowUnfree = true;
@ -60,6 +78,9 @@
chute = chute.packages.${arch}.chute; chute = chute.packages.${arch}.chute;
chuteUnstable = chuteUnstable.packages.${arch}.chute; chuteUnstable = chuteUnstable.packages.${arch}.chute;
}) })
(final: prev: {
nyxt = unstable.nyxt;
})
]; ];
}; };
@ -91,7 +112,9 @@
(config-dir + /site-config/${hostOpts.site}.nix) (config-dir + /site-config/${hostOpts.site}.nix)
]; ];
config = { config = let
pkgs = pkgs-for hostOpts.arch;
in {
instance = let instance = let
build-seed = builtins.readFile build-seed = builtins.readFile
config.fudo.secrets.files.build-seed; config.fudo.secrets.files.build-seed;
@ -99,14 +122,24 @@
inherit hostname build-timestamp build-seed; inherit hostname build-timestamp build-seed;
}; };
nix.registry = { environment.etc.nixos-live.source = ./.;
nix = {
registry = {
nixpkgs.flake = nixpkgs;
fudo-nixos.flake = self; fudo-nixos.flake = self;
fudo-entities.flake = fudo-entities; fudo-entities.flake = fudo-entities;
fudo-lib.flake = fudo-lib; fudo-lib.flake = fudo-lib;
fudo-pkgs.flake = fudo-pkgs; fudo-pkgs.flake = fudo-pkgs;
}; };
nixPath = let
lib = nixpkgs.lib;
in lib.mkDefault (lib.mkBefore [
"nixpkgs=${nixpkgs}"
]);
};
nixpkgs.pkgs = pkgs-for hostOpts.arch; nixpkgs.pkgs = pkgs;
}; };
}; };

View File

@ -7,14 +7,10 @@ let
pkgs = import <nixpkgs> { pkgs = import <nixpkgs> {
config = { config = {
allowUnfree = true; allowUnfree = true;
permittedInsecurePackages = [ permittedInsecurePackages = [ "openssh-with-gssapi-8.4p1" ];
"openssh-with-gssapi-8.4p1"
];
}; };
overlays = [ overlays = [ (import /state/fudo-pkgs/overlay.nix) ];
(import ./fudo-pkgs/overlay.nix)
];
}; };
in { in {
@ -64,8 +60,7 @@ in {
# groups = { wheel = { members = [ "niten" ]; }; }; # groups = { wheel = { members = [ "niten" ]; }; };
}; };
home-manager = let home-manager = let home-generator = pkgs.callPackage ./nix-home { };
home-generator = pkgs.callPackage ./nix-home {};
in { in {
useGlobalPkgs = true; useGlobalPkgs = true;
@ -74,9 +69,7 @@ in {
username = "niten"; username = "niten";
user-email = "niten@fudo.org"; user-email = "niten@fudo.org";
home-dir = "/home/niten"; home-dir = "/home/niten";
}) { }) { enable-gui = false; };
enable-gui = false;
};
}; };
}; };

View File

@ -8,12 +8,12 @@
(define *host-passwd-file* (getenv "FUDO_HOST_PASSWD_FILE")) (define *host-passwd-file* (getenv "FUDO_HOST_PASSWD_FILE"))
(when (not *host-passwd-file*) (when (not *host-passwd-file*)
(format (current-error-port "FUDO_HOST_PASSWD_FILE not set~%")) (format #t (current-error-port "FUDO_HOST_PASSWD_FILE not set~%"))
(exit 1)) (exit 1))
(define *service-passwd-file* (getenv "FUDO_SERVICE_PASSWD_FILE")) (define *service-passwd-file* (getenv "FUDO_SERVICE_PASSWD_FILE"))
(when (not *service-passwd-file*) (when (not *service-passwd-file*)
(format (current-error-port "FUDO_SERVICE_PASSWD_FILE not set~%")) (format #t (current-error-port "FUDO_SERVICE_PASSWD_FILE not set~%"))
(exit 1)) (exit 1))
(define host-regex "^host-([a-zA-Z][a-zA-Z0-9_-]+)$") (define host-regex "^host-([a-zA-Z][a-zA-Z0-9_-]+)$")
@ -35,7 +35,7 @@
(service-verifier (match:substring (string-match service-regex username) 1) (service-verifier (match:substring (string-match service-regex username) 1)
password)) password))
(else #f)))) (else (begin (format #t "unrecognized username: ~s @ ~s~%" username hostname))))))
(define (make-handler handlers) (define (make-handler handlers)
(lambda (request) (lambda (request)