Commit to move

This commit is contained in:
niten 2023-05-16 22:40:08 -07:00
parent f45e9a377a
commit de24170e88
40 changed files with 5188 additions and 936 deletions

View File

@ -1,14 +1,13 @@
{ config, lib, pkgs, ... }:
let
fudo = config.fudo.domains."fudo.org";
let fudo = config.fudo.domains."fudo.org";
in {
config.fudo.domains."sea.fudo.org" = {
local-networks = fudo.local-networks;
gssapi-realm = fudo.gssapi-realm;
kerberos-master = fudo.kerberos-master;
kerberos-slaves = fudo.kerberos-slaves;
# gssapi-realm = fudo.gssapi-realm;
# kerberos-master = fudo.kerberos-master;
# kerberos-slaves = fudo.kerberos-slaves;
primary-mailserver = fudo.primary-mailserver;

75
config/hardware/jazz.nix Normal file
View File

@ -0,0 +1,75 @@
{ config, lib, pkgs, modulesPath, ... }:
{
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
boot = {
initrd = {
availableKernelModules =
[ "nvme" "xhci_pci" "ahci" "usbhid" "usb_storage" "sd_mod" ];
kernelModules = [ ];
};
loader = {
systemd-boot.enable = true;
efi.canTouchEfiVariables = true;
};
kernelModules = [ "kvm-amd" ];
extraModulePackages = [ ];
};
fileSystems = {
"/" = {
device = "jazz-root";
fsType = "tmpfs";
options = [ "mode=755" "noexec" ];
};
"/boot" = {
device = "/dev/disk/by-label/JAZZ-BOOT";
fsType = "vfat";
options = [ "noatime" "noexec" ];
};
"/nix" = {
device = "/dev/disk/by-label/jazz-data";
fsType = "btrfs";
options = [ "subvol=@nix" "noatime" "compress=zstd" ];
};
"/state" = {
device = "/dev/disk/by-label/jazz-data";
fsType = "btrfs";
options = [ "subvol=@state" "noatime" "compress=zstd" ];
};
"/home" = {
device = "/dev/disk/by-label/jazz-data";
fsType = "btrfs";
options = [ "subvol=@home" "noatime" "compress=zstd" ];
};
"/var/log" = {
device = "/dev/disk/by-label/jazz-data";
fsType = "btrfs";
options = [ "subvol=@log" "noatime" "compress=zstd" "noexec" ];
};
};
swapDevices = [{ device = "/dev/disk/by-label/jazz-swap"; }];
hardware = {
enableAllFirmware = true;
cpu.amd.updateMicrocode = true;
};
networking = {
useDHCP = false;
macvlans = {
intif0 = {
interface = "enp5s0";
mode = "bridge";
};
};
interfaces.intif0.macAddress = "02:57:9a:a4:10:d3";
};
}

View File

@ -1,13 +1,22 @@
{ config, lib, pkgs, ... }:
{ config, lib, pkgs, modulesPath, ... }:
with lib;
let
let generateMac = pkgs.lib.network.generate-mac-address;
in {
system.stateVersion = "21.05";
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
config = {
system.stateVersion = "22.05";
boot = {
initrd = {
luks.devices.lambda-unlocked = {
device = "/dev/disk/by-uuid/e90c9dda-4e4c-4ca1-8897-39fcebc03479";
allowDiscards = true;
};
availableKernelModules = [
"uhci_hcd"
"ehci_pci"
@ -18,68 +27,72 @@ in {
"sd_mod"
"sr_mod"
];
kernelModules = [ ];
kernelModules = [ "dm-snapshot" ];
};
kernelModules = [ "kvm-intel" ];
supportedFilesystems = [ "zfs" ];
kernelPackages = pkgs.linuxPackages_latest;
loader.grub = {
enable = true;
version = 2;
device = "/dev/disk/by-id/wwn-0x600508b1001cecf6b880f591f9b18b29";
device = "/dev/disk/by-id/scsi-3600508b1001c3be9174b4bdb31935121";
};
};
fileSystems = {
"/" = {
device = "lambda-root";
fsType = "tmpfs";
options = [ "mode=755" "size=32G" "noexec" ];
};
"/boot" = {
device = "/dev/disk/by-label/lambda-boot";
fsType = "ext4";
options = [ "noexec" "noatime" "nodiratime" ];
options = [ "noatime" "noexec" ];
};
"/" = {
device = "none";
fsType = "tmpfs";
options = [ "noexec" "mode=755" ];
};
"/nix" = {
device = "lambda/transient/nix";
fsType = "zfs";
options = [ "noatime" "nodiratime" ];
};
"/var/log" = {
device = "lambda/transient/logs";
fsType = "zfs";
neededForBoot = true;
options = [ "noexec" "noatime" "nodiratime" ];
};
"/state" = {
device = "lambda/persistent/state";
fsType = "zfs";
options = [ "noexec" "noatime" "nodiratime" ];
device = "/dev/mapper/lambda-unlocked";
fsType = "btrfs";
options = [ "noatime" "compress=zstd" "noexec" "subvol=@state" ];
};
"/nix" = {
device = "/dev/mapper/lambda-unlocked";
fsType = "btrfs";
options = [ "noatime" "compress=zstd" "subvol=@nix" ];
};
"/home" = {
device = "/dev/mapper/lambda-unlocked";
fsType = "btrfs";
options = [ "noatime" "compress=zstd" "noexec" "subvol=@home" ];
};
"/var/log" = {
device = "/dev/mapper/lambda-unlocked";
fsType = "btrfs";
options = [ "noatime" "compress=zstd" "noexec" "subvol=@log" ];
};
};
swapDevices = [{ device = "/dev/disk/by-label/lambda-swap"; }];
swapDevices = [{
device = "/dev/disk/by-id/scsi-3600508b1001c3be9174b4bdb31935121-part2";
randomEncryption.enable = true;
}];
nix.settings.max-jobs = lib.mkDefault 12;
nix.settings.max-jobs = lib.mkDefault 24;
networking = {
useDHCP = false;
macvlans = {
intif0 = {
interface = "enp3s0f1";
interface = "enp4s0f1";
mode = "bridge";
};
};
interfaces = {
intif0 = {
# output of: echo lambda-intif0|md5sum|sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/'
macAddress = "02:f5:fe:8c:22:fe";
macAddress = generateMac config.instance.hostname "intif0";
};
};
};
};

View File

@ -34,7 +34,7 @@
};
};
nix.maxJobs = lib.mkDefault 24;
nix.settings.max-jobs = lib.mkDefault 24;
hardware.bluetooth.enable = false;

View File

@ -35,7 +35,7 @@
swapDevices = [{ device = "/dev/disk/by-label/swap"; }];
nix.maxJobs = lib.mkDefault 8;
nix.settings.max-jobs = lib.mkDefault 8;
hardware.bluetooth.enable = false;

View File

@ -0,0 +1,75 @@
{ config, lib, pkgs, ... }:
{
system.stateVersion = "22.05";
boot = {
loader = {
systemd-boot.enable = true;
efi.canTouchEfiVariables = true;
};
initrd = {
availableKernelModules =
[ "xhci_pci" "ahci" "nvme" "usbhid" "usb_storage" "sd_mod" ];
kernelModules = [ ];
};
kernelModules = [ "kvm-amd" ];
extraModulePackages = [ ];
};
fileSystems = {
"/" = {
device = "toothless-root";
fsType = "tmpfs";
options = [ "mode=755" "noexec" ];
};
"/boot" = {
device = "/dev/disk/by-label/TOOTH-BOOT";
fsType = "vfat";
options = [ "noatime" "noexec" ];
};
"/nix" = {
device = "/dev/disk/by-label/toothless-data";
fsType = "btrfs";
options = [ "subvol=@nix" "noatime" "compress=zstd" ];
};
"/state" = {
device = "/dev/disk/by-label/toothless-data";
fsType = "btrfs";
options = [ "subvol=@state" "noatime" "compress=zstd" "noexec" ];
};
"/home" = {
device = "/dev/disk/by-label/toothless-data";
fsType = "btrfs";
options = [ "subvol=@home" "noatime" "compress=zstd" "noexec" ];
};
"/var/log" = {
device = "/dev/disk/by-label/toothless-data";
fsType = "btrfs";
options = [ "subvol=@log" "noatime" "compress=zstd" "noexec" ];
};
};
swapDevices = [{ device = "/dev/disk/by-label/toothless-swap"; }];
hardware = {
enableAllFirmware = true;
cpu.amd.updateMicrocode = true;
};
networking = {
useDHCP = false;
macvlans = {
intif0 = {
interface = "enp42s0";
mode = "bridge";
};
};
interfaces.intif0.macAddress = "02:ee:76:17:99:ed";
};
}

View File

@ -0,0 +1,41 @@
{ config, lib, pkgs, ... }:
with lib;
let stateDir = "/state";
in {
fudo = { wallfly.location = "family_room"; };
networking = {
interfaces.intif0.useDHCP = true;
firewall.enable = false;
};
systemd.tmpfiles.rules = [
"L /etc/adjtime - - - - ${stateDir}/etc/adjtime"
"d ${stateDir}/lib/cups 755 root root - -"
"d ${stateDir}/lib/flatpak 755 root root - -"
];
fileSystems = {
"/var/lib/cups" = {
device = "${stateDir}/lib/cups";
options = [ "bind" ];
};
"/var/lib/flatpak" = {
device = "${stateDir}/lib/flatpak";
options = [ "bind" ];
};
};
environment.etc = {
nixos.source = "/etc/nixos-live";
NIXOS.source = "${stateDir}/etc/NIXOS";
};
systemd.targets = {
sleep.enable = false;
suspend.enable = false;
hibernate.enable = false;
hybrid-sleep.enable = false;
};
}

View File

@ -1,15 +1,10 @@
{ config, lib, pkgs, ... }:
let
state-dir = "/state"; # This must be a string!
generate-mac = pkgs.lib.network.generate-mac-address;
let primaryIp = "10.0.0.11";
in {
boot = {
loader.grub.copyKernels = true;
#kernelModules = [ "rpcsec_gss_krb5" ];
};
config = {
boot = { loader.grub.copyKernels = true; };
networking = {
interfaces = {
@ -18,59 +13,28 @@ in {
enp4s0f0.useDHCP = false;
enp4s0f1.useDHCP = false;
intif0.useDHCP = true;
intif0 = {
useDHCP = false;
ipv4.addresses = [{
address = primaryIp;
prefixLength = 16;
}];
};
};
systemd.tmpfiles.rules = [
"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.pub - - - - ${state-dir}/user/root/ssh/id_rsa.pub"
"L /root/.ssh/known_hosts - - - - ${state-dir}/user/root/ssh/known_hosts"
];
defaultGateway = {
address = "10.0.0.1";
interface = "intif0";
};
};
services.openssh.hostKeys = [
{
path = "${state-dir}/ssh/ssh_host_rsa_key";
type = "rsa";
bits = 4096;
}
{
path = "${state-dir}/ssh/ssh_host_ed25519_key";
type = "ed25519";
bits = 4096;
}
];
environment.etc = {
"ssh/ssh_host_rsa_key" = {
source = "${state-dir}/ssh/ssh_host_rsa_key";
user = "root";
group = "root";
mode = "0400";
};
"ssh/ssh_host_rsa_key.pub" = {
source = "${state-dir}/ssh/ssh_host_rsa_key.pub";
user = "root";
group = "root";
mode = "0444";
};
"ssh/ssh_host_ed25519_key" = {
source = "${state-dir}/ssh/ssh_host_ed25519_key";
user = "root";
group = "root";
mode = "0400";
};
"ssh/ssh_host_ed25519_key.pub" = {
source = "${state-dir}/ssh/ssh_host_ed25519_key.pub";
user = "root";
group = "root";
mode = "0444";
};
environment = {
etc = {
nixos.source = "/etc/nixos-live";
adjtime.source = "/state/host/adjtime";
NIXOS.source = "/state/host/NIXOS";
};
systemPackages = with pkgs; [ nixopsUnstable openssl ];
};
security.sudo.extraConfig = ''
# Due to rollback, sudo will lecture after every reboot
@ -81,14 +45,28 @@ in {
secrets = {
secret-group = "fudo-secrets";
secret-users = [ "niten" ];
secret-paths = [ "/state/secrets" ];
secret-paths = [ "/secrets" ];
};
hosts.lambda.encrypted-filesystems.secrets = {
encrypted-device =
"/dev/disk/by-id/scsi-3600508b1001c2f439e343270a365a5bd-part1";
key-path = "/state/secrets-key/key";
filesystem-type = "btrfs";
remove-key = false;
type = "luks2";
mountpoints = {
"/secrets" = {
options = [ "noatime" "compress=zstd" ];
group = "fudo-secrets";
users = [ "niten" ];
world-readable = false;
};
};
};
};
minecraft-clj = {
enable = true;
state-directory = "/state/services/minecraft-clj";
admins = [ "fudoniten" ];
worlds = { REPLand = { allocated-memory = 16; }; };
systemd = {
tmpfiles.rules = [ "L /etc/adjtime - - - - /state/etc/adjtime" ];
};
};
}

View File

@ -55,8 +55,8 @@ in {
ldap.state-directory = "/state/auth/ldap";
kerberos = {
state-directory = "/state/auth/kerberos";
master-key-file = host-secrets.heimdal-master-key.target-file;
ipropd-keytab = host-secrets.heimdal-ipropd-keytab.target-file;
# master-key-file = host-secrets.heimdal-master-key.target-file;
# ipropd-keytab = host-secrets.heimdal-ipropd-keytab.target-file;
};
};
@ -64,15 +64,17 @@ in {
state-directory = "/state/services/chat";
external-interface = "extif0";
};
nexus.dns-server.listen-addresses = [ host-ipv4 ];
};
secrets.host-secrets.legatus = let files = config.fudo.secrets.files;
in {
postgres-keytab = {
source-file = files.service-keytabs.procul.postgres;
target-file = "/srv/postgres/secure/postgres.keytab";
user = "root";
};
# postgres-keytab = {
# source-file = files.service-keytabs.procul.postgres;
# target-file = "/srv/postgres/secure/postgres.keytab";
# user = "root";
# };
# gitea-database-password = {
# source-file = files.service-passwords.procul.gitea-database;
@ -80,17 +82,17 @@ in {
# user = config.fudo.git.user;
# };
heimdal-master-key = {
source-file = files.realm-master-keys."FUDO.ORG";
target-file = "/run/heimdal/master-key";
user = config.fudo.auth.kdc.user;
};
# heimdal-master-key = {
# source-file = files.realm-master-keys."FUDO.ORG";
# target-file = "/run/heimdal/master-key";
# user = config.fudo.auth.kdc.user;
# };
heimdal-ipropd-keytab = {
source-file = files.service-keytabs.legatus.ipropd;
target-file = "/run/heimdal/ipropd.keytab";
user = config.fudo.auth.kdc.user;
};
# heimdal-ipropd-keytab = {
# source-file = files.service-keytabs.legatus.ipropd;
# target-file = "/run/heimdal/ipropd.keytab";
# user = config.fudo.auth.kdc.user;
# };
};
client.dns = {

View File

@ -93,6 +93,8 @@ in {
prometheus.state-directory = "/state/services/prometheus";
};
auth.kerberos.state-directory = "/state/services/heimdal-kdc";
# wireguard-gateway = {
# enable = true;
# network = "10.0.200.0/24";

View File

@ -21,12 +21,19 @@ in {
firewall.enable = false;
};
environment.systemPackages = [ pkgs.kdcConvertDatabase ];
# Hopefully this'll help with NFS...
boot.kernelModules = [ "rpcsec_gss_krb5" ];
services = {
murmur.enable = true;
# objectifier = {
# enable = true;
# listen-addresses = [ "0.0.0.0" ];
# };
nfs = {
# See ../user-config.nix for the user@REALM -> user mapping
server = {
@ -117,6 +124,8 @@ in {
};
ldap.base-dn = "dc=fudo,dc=org";
};
auth.kerberos.state-directory = "/state/services/heimdal-kdc";
};
postgresql = {

View File

@ -7,7 +7,7 @@
data-dir = "/state/minecraft/data";
world-name = "selbyland";
game-mode = "creative";
difficulty = 0;
difficulty = 2;
allow-cheats = true;
allocated-memory = 14;
};

View File

@ -1,6 +1,6 @@
{ config, lib, pkgs, ... }:
{ config, pkgs, ... }:
with lib;
with pkgs.lib;
let
hostname = "nutboy3";
host-fqdn = config.instance.host-fqdn;
@ -32,6 +32,8 @@ in {
];
config = {
boot.kernelModules = [ "veth" ];
networking = {
nameservers = [ "1.1.1.1" ];
defaultGateway = {
@ -65,24 +67,22 @@ in {
secrets.host-secrets.${hostname} = let files = config.fudo.secrets.files;
in {
heimdal-master-key = {
source-file = files.realm-master-keys."FUDO.ORG";
target-file = "/run/heimdal/master-key";
user = config.fudo.auth.kdc.user;
};
# heimdal-master-key = {
# source-file = files.realm-master-keys."FUDO.ORG";
# target-file = "/run/heimdal/master-key";
# user = config.fudo.auth.kdc.user;
# };
ldap-keytab = {
source-file = files.service-keytabs.${hostname}.openldap;
# files.service-keytabs.${hostname}.openldap;
source-file = extractFudoKeytab {
realm = domain.gssapi-realm;
principals = [ "ldap/${host-fqdn}" ];
};
target-file = "/run/openldap/ldap.keytab";
user = config.services.openldap.user;
};
postgresql-keytab = {
source-file = files.service-keytabs.nutboy3.postgres;
target-file = "/run/postgresql/postgres.keytab";
user = postgresql-user;
};
grafana-database-password = {
source-file = grafana-database-passwd-file;
target-file = "/run/metrics/grafana/db.passwd";
@ -129,15 +129,17 @@ in {
auth = {
ldap.state-directory = "/state/auth/ldap";
kerberos = {
state-directory = "/state/auth/kerberos";
master-key-file = host-secrets.heimdal-master-key.target-file;
state-directory = "/state/services/heimdal-kdc";
# 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;
keytab = extractFudoKeytab {
realm = domain.gssapi-realm;
principals = [ "postgres/${host-fqdn}" ];
};
};
metrics = {

View File

@ -6,19 +6,14 @@ let
host-ipv4 = "199.87.154.175";
local-packages = with pkgs; [
bind
emacs-nox
mtr
vim
];
local-packages = with pkgs; [ bind emacs-nox mtr vim ];
fudo-zone = pkgs.lib.dns.zoneToZonefile
config.instance.build-timestamp "fudo.org"
fudo-zone =
pkgs.lib.dns.zoneToZonefile config.instance.build-timestamp "fudo.org"
config.fudo.zones."fudo.org";
selby-zone = pkgs.lib.dns.zoneToZonefile
config.instance.build-timestamp "selby.ca"
selby-zone =
pkgs.lib.dns.zoneToZonefile config.instance.build-timestamp "selby.ca"
config.fudo.zones."selby.ca";
in {
@ -36,12 +31,14 @@ in {
{
ipv4-address = "209.177.102.102";
ipv6-address = "2001:470:1f16:40::2";
description = "Nameserver 2, Musashi.100percenthost.net, in Winnipeg, MB, CA";
description =
"Nameserver 2, Musashi.100percenthost.net, in Winnipeg, MB, CA";
}
{
ipv4-address = "104.131.53.95";
ipv6-address = "2604:a880:800:10::8:7001";
description = "Nameserver 3, ns2.henchmman21.net, in New York City, NY, US";
description =
"Nameserver 3, ns2.henchmman21.net, in New York City, NY, US";
}
{
ipv4-address = "204.42.254.5";
@ -52,8 +49,8 @@ in {
};
"selby.ca" = {
enable = true;
external-nameservers = map (n: let
i = toString n;
external-nameservers = map (n:
let i = toString n;
in {
authoritative-hostname = "ns${i}.fudo.org";
description = "Nameserver ${i}, ns${i}.fudo.org.";
@ -82,9 +79,7 @@ in {
"rspamd._metrics._tcp IN SRV 0 0 443 mail.fudo.org."
];
};
"selby.ca" = {
default-host = host-ipv4;
};
"selby.ca" = { default-host = host-ipv4; };
};
};
@ -117,17 +112,15 @@ in {
interfaces = [ "eno2" ];
config = { config, ... }: {
boot.kernelModules = [ "veth" ];
nixpkgs.pkgs = pkgs;
environment = {
systemPackages = local-packages;
etc = {
"generated-zones/fudo.org" = {
text = fudo-zone;
};
"generated-zones/selby.ca" = {
text = selby-zone;
};
"generated-zones/fudo.org" = { text = fudo-zone; };
"generated-zones/selby.ca" = { text = selby-zone; };
};
};
@ -144,17 +137,8 @@ in {
];
};
groups = {
wheel.members = [
"niten"
"reaper"
];
dns = {
members = [
"niten"
"reaper"
"named"
];
};
wheel.members = [ "niten" "reaper" ];
dns = { members = [ "niten" "reaper" "named" ]; };
};
};

View File

@ -1,6 +1,6 @@
{ config, lib, pkgs, ... }:
with lib;
with pkgs.lib;
let
hostname = "procul";
@ -58,16 +58,14 @@ in {
networking.firewall.allowedTCPPorts = [ 80 443 ];
security.acme.email = "viator@informis.land";
security.acme.defaults.email = "viator@informis.land";
users = {
users = {
gituser = {
users.gituser = {
isSystemUser = true;
group = "nogroup";
};
};
groups = { acme = { members = [ "nginx" ]; }; };
groups.acme.members = [ "nginx" ];
};
informis = {
@ -154,11 +152,11 @@ in {
user = config.services.postgresql.superUser;
};
heimdal-master-key = {
source-file = files.realm-master-keys."INFORMIS.LAND";
target-file = "/run/heimdal/master-key";
user = config.fudo.auth.kdc.user;
};
# heimdal-master-key = {
# source-file = files.realm-master-keys."INFORMIS.LAND";
# target-file = "/run/heimdal/master-key";
# user = config.fudo.auth.kdc.user;
# };
chute-staging-credentials = {
source-file = files.service-secrets.procul."chute-staging.env";
@ -260,7 +258,11 @@ in {
};
postgresql = {
state-directory = "/state/services/postgresql";
keytab = config.fudo.secrets.files.service-keytabs.procul.postgres;
keytab = extractFudoHostKeytab {
inherit hostname;
realm = domain.gssapi-realm;
services = [ "postgres" ];
};
};
logging.loki.state-directory = "/state/services/loki";
metrics = {

View File

@ -1,6 +1,8 @@
{ config, lib, pkgs, ... }:
with lib; {
with lib;
let state-dir = "/state";
in {
fudo = {
slynk.enable = true;
wallfly.location = "office";
@ -16,6 +18,15 @@ with lib; {
fcitx5.addons = with pkgs; [ fcitx5-chinese-addons fcitx5-rime ];
};
systemd.tmpfiles.rules = [ "d ${state-dir}/lib/cups 755 root root - -" ];
fileSystems = {
"/var/lib/cups" = {
device = "${state-dir}/lib/cups";
options = [ "bind" ];
};
};
# fudo.adguard-dns-proxy = {
# enable = true;
# http.listen-ip = "10.0.0.108";

View File

@ -20,6 +20,7 @@ in {
"L /root/.ssh/known_hosts - - - - ${state-dir}/user/root/ssh/known_hosts"
"L /var/lib/flatpak - - - - ${state-dir}/lib/flatpak"
"L /etc/adjtime - - - - ${state-dir}/etc/adjtime"
"d ${state-dir}/lib/cups 755 root root - -"
];
services = {
@ -38,6 +39,13 @@ in {
];
};
fileSystems = {
"/var/lib/cups" = {
device = "${state-dir}/lib/cups";
options = [ "bind" ];
};
};
environment.etc = {
nixos.source = "/etc/nixos-live";
NIXOS.source = "${state-dir}/etc/NIXOS";

View File

@ -0,0 +1,55 @@
{ config, lib, pkgs, ... }:
let
stateDir = "/state";
primaryIp = "10.0.0.12";
generateMac = pkgs.lib.network.generate-mac-address;
in {
networking = {
useDHCP = false;
defaultGateway = {
address = "10.0.0.1";
interface = "intif0";
};
interfaces.intif0 = {
ipv4.addresses = [{
address = primaryIp;
prefixLength = 16;
}];
};
};
security.sudo.extraConfig = ''
# Due to rollback, sudo will lecture after every reboot
Defaults lecture = never
'';
fudo = {
minecraft-clj = {
enable = true;
state-directory = "/state/services/minecraft-clj";
admins = [ "fudoniten" ];
worlds = {
REPLand = { allocated-memory = 8; };
wof = {
world-name = "WorldOfFun";
world-seed = 2059666523504992;
port = 25567;
difficulty = "medium";
game-mode = "survival";
motd = "Welcome to the World of Fun!";
allow-cheats = true;
allocated-memory = 16;
pvp = false;
};
};
};
};
systemd.targets = {
sleep.enable = false;
suspend.enable = false;
hibernate.enable = false;
hybrid-sleep.enable = false;
};
}

View File

@ -6,13 +6,10 @@ let
primary-ip = "10.0.0.3";
state-dir = "/state";
zigbee2mqtt-statedir = "${state-dir}/services/zigbee2mqtt";
mosquitto-statedir = "${state-dir}/services/mosquitto";
home-assistant-port = 8123;
zigbee2mqtt-user = config.systemd.services.zigbee2mqtt.serviceConfig.User;
mosquitto-user = config.systemd.services.mosquitto.serviceConfig.User;
zigbee2mqtt-passwd-file =
pkgs.lib.passwd.stablerandom-passwd-file "zigbee2mqtt-passwd"
config.instance.build-seed;
@ -54,31 +51,21 @@ in {
dhcpcd.extraConfig = concatStringsSep "\n" [ "nogateway" ];
};
fudo.secrets.host-secrets.${hostname} = {
mosquitto-zigbee2mqtt-passwd = {
source-file = zigbee2mqtt-passwd-file;
target-file = "/run/mosquitto-secrets/zigbee2mqtt.passwd";
user = mosquitto-user;
fudo.services.mqtt = {
enable = true;
state-directory = "${state-dir}/services/mosquitto";
private = {
enable = true;
users = {
zigbee2mqtt = {
password-file = zigbee2mqtt-passwd-file;
acl = [ "readwrite #" ];
};
mosquitto-home-assistant-passwd = {
source-file = host-passwds.mosquitto-home-assistant;
target-file = "/run/mosquitto-secrets/home-assistant.passwd";
user = mosquitto-user;
home-assistant = {
password-file = host-passwds.mosquitto-home-assistant;
acl = [ "readwrite #" ];
};
mosquitto-niten-passwd = {
source-file = host-passwds.mosquitto-niten;
target-file = "/run/mosquitto-secrets/niten.passwd";
user = mosquitto-user;
};
mosquitto-xiaoxuan-passwd = {
source-file = host-passwds.mosquitto-xiaoxuan;
target-file = "/run/mosquitto-secrets/xiaoxuan.passwd";
user = mosquitto-user;
};
mosquitto-wallfly-passwd = {
source-file = host-passwds.mosquitto-wallfly;
target-file = "/run/mosquitto-secrets/wallfly.passwd";
user = mosquitto-user;
};
};
@ -95,15 +82,6 @@ in {
RemainAfterExit = true;
};
};
zigbee2mqtt = {
after = [ config.fudo.secrets.secret-target "mosquitto.service" ];
restartIfChanged = true;
};
mosquitto = {
after = [ config.fudo.secrets.secret-target ];
restartIfChanged = true;
};
};
tmpfiles.rules = [
@ -114,7 +92,6 @@ in {
"L /etc/adjtime - - - - ${state-dir}/etc/adjtime"
"d /state/services 0711 root root - -"
"d ${zigbee2mqtt-statedir} 0700 ${zigbee2mqtt-user} - - -"
"d ${mosquitto-statedir} 0700 ${mosquitto-user} - - -"
];
};
@ -149,39 +126,39 @@ in {
};
};
mosquitto = {
enable = true;
dataDir = mosquitto-statedir;
listeners = [{
settings.allow_anonymous = false;
port = 1883;
address = "0.0.0.0";
users = {
zigbee2mqtt = {
passwordFile =
host-secrets.mosquitto-zigbee2mqtt-passwd.target-file;
acl = [ "readwrite #" ];
};
home-assistant = {
passwordFile =
host-secrets.mosquitto-home-assistant-passwd.target-file;
acl = [ "readwrite #" ];
};
# mosquitto = {
# enable = true;
# dataDir = mosquitto-statedir;
# listeners = [{
# settings.allow_anonymous = false;
# port = 1883;
# address = "0.0.0.0";
# users = {
# zigbee2mqtt = {
# passwordFile =
# host-secrets.mosquitto-zigbee2mqtt-passwd.target-file;
# acl = [ "readwrite #" ];
# };
# home-assistant = {
# passwordFile =
# host-secrets.mosquitto-home-assistant-passwd.target-file;
# acl = [ "readwrite #" ];
# };
# niten = {
# passwordFile = host-secrets.mosquitto-niten-passwd.target-file;
# acl = [ "readwrite #" ];
# };
# xiaoxuan = {
# passwordFile = host-secrets.mosquitto-xiaoxuan-passwd.target-file;
# acl = [ "readwrite #" ];
# # xiaoxuan = {
# # passwordFile = host-secrets.mosquitto-xiaoxuan-passwd.target-file;
# # acl = [ "readwrite #" ];
# # };
# # wallfly = {
# # passwordFile = host-secrets.mosquitto-wallfly-passwd.target-file;
# # acl = [ "readwrite homeassistant/binary_sensor/#" ];
# # };
# };
# wallfly = {
# passwordFile = host-secrets.mosquitto-wallfly-passwd.target-file;
# acl = [ "readwrite homeassistant/binary_sensor/#" ];
# }];
# };
};
}];
};
zigbee2mqtt = {
enable = true;
@ -191,8 +168,11 @@ in {
homeassistant = true;
permit_join = true;
serial.port = "/dev/ttyUSB0";
mqtt = {
server = "mqtt://127.0.0.1:1883";
mqtt = let
mqttHost = config.fudo.services.mqtt.mqtt-hostname;
mqttPort = config.fudo.services.mqtt.private.port;
in {
server = "mqtt://${mqttHost}:${toString mqttPort}";
user = "zigbee2mqtt";
password = readFile zigbee2mqtt-passwd-file;
# TODO: could make a yaml file containing password

View File

@ -1,5 +1,6 @@
{ config, lib, pkgs, ... }:
with lib;
let state-dir = "/state";
in {
config = {
@ -19,12 +20,20 @@ in {
};
systemd.tmpfiles.rules = [
"d ${state-dir}/lib/cups 755 root root - -"
"d ${state-dir}/lib/flatpak 0755 root root - -"
"d ${state-dir}/etc 0755 root root - -"
"L /var/lib/flatpak - - - - ${state-dir}/lib/flatpak"
"L /etc/adjtime - - - - ${state-dir}/etc/adjtime"
];
fileSystems = {
"/var/lib/cups" = {
device = "${state-dir}/lib/cups";
options = [ "bind" ];
};
};
hardware = {
bluetooth = {
enable = true;
@ -32,5 +41,11 @@ in {
};
xpadneo.enable = true;
};
services.xserver = {
layout = "us";
xkbVariant = mkForce "";
xkbOptions = mkForce "";
};
};
}

View File

@ -1,10 +1,10 @@
{ config, lib, pkgs, ... }:
with lib;
let
has-secret-files = hasAttr "files" config.fudo.secrets;
let has-secret-files = hasAttr "files" config.fudo.secrets;
in {
config.instance = mkIf has-secret-files {
# TODO: This has a newline, I think...
build-seed = builtins.readFile config.fudo.secrets.files.build-seed;
};
}

View File

@ -106,4 +106,7 @@ in {
};
in [ factorio ];
};
fudo.services.tattler.enable-notifications =
trace "${hostname}: ${toString enable-gui}" enable-gui;
}

View File

@ -31,7 +31,7 @@ in {
in concatMap nix-files import-paths;
config = {
fudo.hosts.${hostname}.local-networks = [ "::1/128" ];
fudo = { hosts.${hostname}.local-networks = [ "::1/128" ]; };
system.autoUpgrade.enable = false;
@ -49,28 +49,7 @@ in {
nixpkgs.config.allowUnfree = 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.heimdal;
};
hardware.enableAllFirmware = true;
services = {
openssh = {
@ -78,12 +57,12 @@ in {
startWhenNeeded = true;
useDns = true;
permitRootLogin = "prohibit-password";
extraConfig = ''
GSSAPIAuthentication yes
GSSAPICleanupCredentials yes
GSSAPIKeyExchange yes
GSSAPIStoreCredentialsOnRekey yes
'';
# extraConfig = ''
# GSSAPIAuthentication yes
# GSSAPICleanupCredentials yes
# GSSAPIKeyExchange yes
# GSSAPIStoreCredentialsOnRekey yes
# '';
# FIXME: This is temporary! Getting error: Unsupported KEX algorithm "sntrup761x25519-sha512@openssh.com"
kexAlgorithms = [
"curve25519-sha256"
@ -102,7 +81,7 @@ in {
xserver = {
layout = "us";
xkbVariant = "dvp";
xkbOptions = "";
xkbOptions = "ctrl:nocaps";
};
btrfs.autoScrub.enable = let
@ -111,7 +90,10 @@ in {
in length btrfsFilesystems > 0;
pcscd.enable = true;
udev.packages = with pkgs; [ yubikey-personalization ];
udev = {
enable = true;
packages = with pkgs; [ yubikey-personalization ];
};
};
networking.firewall = {
@ -139,16 +121,7 @@ in {
# pinentryFlavor = if cfg.enable-gui then "gnome3" else "curses";
};
ssh = {
startAgent = true;
package = pkgs.openssh_gssapi;
extraConfig = ''
GSSAPIAuthentication yes
GSSAPIDelegateCredentials yes
'';
};
ssh = { startAgent = true; };
};
security = {

View File

@ -7,8 +7,9 @@ let
try-attr = attr: set: if (hasAttr attr set) then set.${attr} else null;
in {
config = mkIf has-secret-files
(let keytab-file = try-attr hostname config.fudo.secrets.files.host-keytabs;
config = mkIf has-secret-files (let
keytab-file =
try-attr hostname config.fudo.secrets.files.kerberos.host-keytabs;
in mkIf (keytab-file != null) {
## This doesn't seem to work...timing?
# environment.etc."krb5.keytab" = mkIf (keytab-file != null) {
@ -19,6 +20,22 @@ in {
# mode = "0400";
# };
krb5 = {
domain_realm = let
krbDoms = filterAttrs (_: domCfg: domCfg.gssapi-realm != null)
config.fudo.domains;
domClauses = dom: domCfg: [
(nameValuePair dom domCfg.gssapi-realm)
(nameValuePair ".${dom}" domCfg.gssapi-realm)
];
concatMapAttrs = f: lst:
listToAttrs (concatMap (i: i) (mapAttrsToList f lst));
in concatMapAttrs domClauses krbDoms;
libdefaults.default_etypes =
"aes128-cts-hmac-sha1-96 aes256-cts-hmac-sha1-96";
};
systemd = let
host-keytab =
config.fudo.secrets.host-secrets.${hostname}.host-keytab.target-file;

View File

@ -8,16 +8,17 @@ let
has-secret-files = hasAttr "files" config.fudo.secrets;
in {
config = mkIf has-secret-files
(let
config = mkIf has-secret-files (let
host-keypairs =
if (hasAttr hostname config.fudo.secrets.files.host-ssh-keypairs) then
config.fudo.secrets.files.host-ssh-keypairs.${hostname}
else [];
if (hasAttr hostname config.fudo.secrets.files.ssh.host-keypairs) then
config.fudo.secrets.files.ssh.host-keypairs.${hostname}
else
[ ];
in {
fudo = let
sshfp-filename = host: keypair: "ssh-${host}-${keypair.key-type}.sshfp-record";
sshfp-filename = host: keypair:
"ssh-${host}-${keypair.key-type}.sshfp-record";
dns-sshfp-records = host: keypair:
pkgs.stdenv.mkDerivation {
@ -27,36 +28,33 @@ in {
buildInputs = with pkgs; [ openssh ];
installPhase =
"ssh-keygen -r REMOVEME -f \"${keypair.public-key}\" | sed 's/^REMOVEME IN SSHFP //' > $out";
installPhase = ''
ssh-keygen -r REMOVEME -f "${keypair.public-key}" | sed 's/^REMOVEME IN SSHFP //' > $out'';
};
host-cfg = config.fudo.hosts.${hostname};
in {
secrets.host-secrets.${hostname} = listToAttrs
(map
(keypair: nameValuePair "host-${keypair.key-type}-private-key" {
secrets.host-secrets.${hostname} = listToAttrs (map (keypair:
nameValuePair "host-${keypair.key-type}-private-key" {
source-file = keypair.private-key;
target-file = "/run/openssh/private/host-${keypair.key-type}-private-key";
target-file =
"/run/openssh/private/host-${keypair.key-type}-private-key";
user = "root";
})
host-keypairs);
}) host-keypairs);
hosts = mkIf (hasAttr "files" config.fudo.secrets)
(mapAttrs (hostname: keypairs: {
hosts = mkIf (hasAttr "files" config.fudo.secrets) (mapAttrs
(hostname: keypairs: {
ssh-pubkeys = map (keypair: keypair.public-key) keypairs;
ssh-fingerprints = concatMap (keypair:
let
fingerprint-derivation = dns-sshfp-records hostname keypair;
let fingerprint-derivation = dns-sshfp-records hostname keypair;
in read-lines "${fingerprint-derivation}") keypairs;
}) config.fudo.secrets.files.host-ssh-keypairs);
}) config.fudo.secrets.files.ssh.host-keypairs);
};
services.openssh.hostKeys = let
host-secrets = config.fudo.secrets.host-secrets.${hostname};
services.openssh.hostKeys =
let host-secrets = config.fudo.secrets.host-secrets."${hostname}";
in map (keypair: {
path =
host-secrets."host-${keypair.key-type}-private-key".target-file;
path = host-secrets."host-${keypair.key-type}-private-key".target-file;
type = keypair.key-type;
}) host-keypairs;
});

View File

@ -3,16 +3,16 @@
with lib;
let
hostname = config.instance.hostname;
domain-name = config.fudo.hosts.${hostname}.domain;
domain = config.fudo.domains.${domain-name};
zone-name = config.fudo.domains.${domain-name}.zone;
domain-name = config.fudo.hosts."${hostname}".domain;
domain = config.fudo.domains."${domain-name}";
zone-name = config.fudo.domains."${domain-name}".zone;
host-fqdn = hostname: "${hostname}.${config.fudo.hosts.${hostname}.domain}";
host-fqdn = hostname: "${hostname}.${domain-name}";
postgresql-server = domain.postgresql-server;
isDatabase = hostname == postgresql-server;
isJabber = elem hostname domain.xmpp-servers;
isDatabaseServer = hostname == postgresql-server;
isJabberServer = elem hostname domain.xmpp-servers;
isDNSBackplane = hostname == domain.backplane.dns-service;
backplaneEnabled = domain.backplane != null;
isNameserver = hostname == domain.backplane.nameserver;
@ -73,18 +73,18 @@ in {
user = config.fudo.backplane.dns.user;
};
database-powerdns-passwd = mkIf isDatabase {
database-powerdns-passwd = mkIf isDatabaseServer {
source-file = powerdns-password;
target-file = "/run/postgres/powerdns.passwd";
user = config.services.postgresql.superUser;
};
database-backplane-passwd = mkIf isDatabase {
database-backplane-passwd = mkIf isDatabaseServer {
source-file = backplane-database-password;
target-file = "/run/postgres/backplane-database.passwd";
user = config.services.postgresql.superUser;
};
ejabberd-backplane-passwd = mkIf isJabber {
ejabberd-backplane-passwd = mkIf isJabberServer {
source-file = xmpp-password;
target-file = "/run/backplane-jabber/service-dns.passwd";
user = config.services.ejabberd.user;
@ -106,7 +106,7 @@ in {
aliases = { backplane = "${backplane-host-fqdn}."; };
};
postgresql = mkIf isDatabase {
postgresql = mkIf isDatabaseServer {
required-services = [ "fudo-passwords.target" ];
users = {
@ -136,7 +136,7 @@ in {
};
backplane = {
enable = isJabber;
enable = isJabberServer;
client-hosts = mapAttrs (hostname: hostOpts: {
password-file = host-password-files.${hostname};

View File

@ -6,6 +6,18 @@ let
cfg = config.fudo.services.dns;
host-secrets = config.fudo.secrets.host-secrets."${hostname}";
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;
zoneKeySecret = zone: "${zone}-ksk";
nameserverOpts = { name, ... }: {
options = with types; {
hostname = mkOption {
@ -39,8 +51,8 @@ let
};
};
zoneOpts = { name, ... }: let
zone-name = name;
zoneOpts = { name, ... }:
let zone-name = name;
in {
options = with types; {
enable = mkOption {
@ -51,7 +63,8 @@ let
default-host = mkOption {
type = nullOr str;
description = "IP which will respond to requests for the base domain.";
description =
"IP which will respond to requests for the base domain.";
default = null;
};
@ -66,11 +79,31 @@ let
description = "Domain which this zone serves.";
default = zone-name;
};
};
};
pthru = obj:
builtins.trace "TRACE: ${ obj }" obj;
ksk = mkOption {
type = nullOr (submodule {
options = {
private-key = mkOption {
type = path;
description = "KSK private key.";
};
public-key = mkOption {
type = path;
description = "KSK public key.";
};
ds = mkOption {
type = path;
description = "KSK ds record.";
};
};
});
description =
"Location of the zone-signing private & public keys and DS record.";
default =
toplevel.config.fudo.secrets.files.dns.key-signing-keys."${zone-name}";
};
};
};
in {
options.fudo.services.dns = with types; {
@ -82,13 +115,20 @@ in {
};
config.fudo = {
zones = mapAttrs (zone-name: zone-cfg: let
secrets.host-secrets."${hostname}" = mkIf is-primary-nameserver (mapAttrs'
(zone: zone-cfg:
nameValuePair (zoneKeySecret zone) {
source-file = zone-cfg.ksk.private-key;
target-file = "/run/nsd/${baseNameOf zone-cfg.ksk.private-key}";
user = config.fudo.nsd.user;
}) cfg.zones);
zones = mapAttrs (zone-name: zone-cfg:
let
domain-name = zone-cfg.domain;
domain = config.fudo.domains.${domain-name};
make-srv-record = port: host: {
inherit port host;
};
make-srv-record = port: host: { inherit port host; };
served-domain = domain.primary-nameserver != null;
@ -96,8 +136,8 @@ in {
is-primary-nameserver = hostname == primary-nameserver;
internal-nameserver-hostnames =
[domain.primary-nameserver] ++ domain.secondary-nameservers;
internal-nameserver-hostnames = [ domain.primary-nameserver ]
++ domain.secondary-nameservers;
get-host-deets = description: hostname: {
ipv4-address = pkgs.lib.network.host-ipv4 config hostname;
@ -105,7 +145,8 @@ in {
description = description;
};
get-ns-deets = hostname: let
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;
@ -115,13 +156,11 @@ in {
in internal-nameservers ++ zone-cfg.external-nameservers;
has-auth-hostname = ns-host: ns-opts:
(hasAttr "authoritative-hostname" ns-opts) &&
(ns-opts.authoritative-hostname != null);
(hasAttr "authoritative-hostname" ns-opts)
&& (ns-opts.authoritative-hostname != null);
all-nameservers = listToAttrs
(imap1
(i: nsOpts:
nameValuePair "ns${toString i}" nsOpts)
(imap1 (i: nsOpts: nameValuePair "ns${toString i}" nsOpts)
nameserver-deets);
nameserver-aliases =
@ -134,49 +173,51 @@ in {
all-nameservers);
dns-srv-records = let
nameserver-srv-records = mapAttrsToList
(hostname: hostOpts: let
nameserver-srv-records = mapAttrsToList (hostname: hostOpts:
let
target-host = if (has-auth-hostname hostname hostOpts) then
"${hostOpts.authoritative-hostname}" else
"${hostOpts.authoritative-hostname}"
else
"${hostname}.${domain-name}";
in make-srv-record 53 target-host)
all-nameservers;
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)];
};
};
# # 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 {
gssapi-realm = domain.gssapi-realm;
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;
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;
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}";
@ -187,18 +228,15 @@ in {
(attrNames nameserver-hosts);
in internal ++ direct-external;
srv-records = dns-srv-records // mail-srv-records;
srv-records = dns-srv-records; # // mail-srv-records;
verbatim-dns-records = mkIf (zone-cfg.ksk != null) [
(readFile zone-cfg.ksk.public-key)
(readFile zone-cfg.ksk.ds)
];
}) 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 {
dns = {
enable = is-primary-nameserver;
identity = "${hostname}.${domain-name}";
@ -207,9 +245,9 @@ in {
(pkgs.lib.network.host-ips config hostname);
domains = mapAttrs' (zone-name: zone-cfg:
nameValuePair zone-cfg.domain
{
dnssec = true;
nameValuePair zone-cfg.domain {
dnssec = zone-cfg.ksk != null;
ksk.key-file = host-secrets."${zoneKeySecret zone-name}".target-file;
zone-definition = config.fudo.zones.${zone-name};
}) cfg.zones;
};

View File

@ -1,11 +1,13 @@
{ config, lib, pkgs, ... }:
{ config, pkgs, ... }:
with lib;
with pkgs.lib;
let
hostname = config.instance.hostname;
domain-name = config.fudo.services.auth.domain;
domain = config.fudo.domains.${domain-name};
realm = domain.gssapi-realm;
zone-name = domain.zone;
ldap-server = elem hostname domain.ldap-servers;
@ -19,6 +21,11 @@ let
cfg = config.fudo.services.auth;
host-secrets = config.fudo.secrets.host-secrets."${hostname}";
krb-user = config.fudo.auth.kerberos.user;
krb-group = config.fudo.auth.kerberos.group;
in {
options.fudo.services.auth = with types; {
domain = mkOption {
@ -30,20 +37,23 @@ in {
ldap = {
hostname = mkOption {
type = str;
description = "Fully-qualified (and public-addressable) domain name of this host.";
description =
"Fully-qualified (and public-addressable) domain name of this host.";
default = config.instance.host-fqdn;
};
state-directory = mkOption {
type = str;
description = "Directory at which to store peristent ldap-related data.";
description =
"Directory at which to store peristent ldap-related data.";
};
};
kerberos = {
hostname = mkOption {
type = str;
description = "Fully-qualified (and public-addressable) domain name of this host.";
description =
"Fully-qualified (and public-addressable) domain name of this host.";
default = config.instance.host-fqdn;
};
@ -56,15 +66,64 @@ in {
type = str;
description = "Path (on the build server) to the KDC master key file.";
};
ipropd-keytab = mkOption {
type = nullOr str;
description = "ipropd keytab for kerberos database propagation.";
};
};
};
config = {
systemd = {
tmpfiles.rules = mkIf (kerberos-master || kerberos-slave) [
"d ${cfg.kerberos.state-directory} 0700 ${krb-user} ${krb-group} - -"
];
paths.heimdal-kdc-initialize = mkIf kerberos-master {
wantedBy = [ "heimdal-kdc.service" ];
pathConfig = {
PathModified = host-secrets.kdc-principals.target-file;
};
};
services = {
heimdal-kdc-initialize = mkIf (kerberos-master || kerberos-slave) {
requires = [
host-secrets.kdc-principals.service
host-secrets.realm-master-key.service
];
description = "Initialize and update the Heimdal KDC database.";
path = with pkgs; [ kdcMergePrincipals coreutils ];
serviceConfig = {
User = krb-user;
Group = krb-group;
ExecStart = let
db = config.fudo.auth.kerberos.kdc.database;
principals = host-secrets.kdc-principals.target-file;
master-key = host-secrets.realm-master-key.target-file;
init-db-cmd = concatStringsSep " " [
"${pkgs.kdcMergePrincipals}/bin/kdc-merge-principals"
"--create"
"--database=${db}"
"--principals=${principals}"
"--key=${master-key}"
"--realm=${realm}"
"--verbose"
];
in pkgs.writeShellScript "heimdal-kdc-initialize.sh" ''
${init-db-cmd}
chown ${krb-user}:${krb-group} ${db}
chmod 0700 ${db}
'';
};
};
heimdal-kdc = mkIf kerberos-master {
requires = [ "heimdal-kdc-initialize.service" ];
after = [ "heimdal-kdc-initialize.service" ];
};
heimdal-kdc-secondary = mkIf kerberos-slave {
requires = [ "heimdal-kdc-initialize.service" ];
after = [ "heimdal-kdc-initialize.service" ];
};
};
};
fudo = {
acme.host-domains.${hostname} = mkIf (ldap-server) {
${cfg.ldap.hostname}.local-copies.openldap = {
@ -73,9 +132,66 @@ in {
};
};
secrets.host-secrets."${hostname}" = let
realm-key =
config.fudo.secrets.files.kerberos.realm-master-keys."${realm}";
in {
realm-master-key = mkIf (kerberos-master || kerberos-slave) {
source-file = realm-key;
target-file = "/run/kdc/realm.key";
user = krb-user;
group = krb-group;
};
kdc-principals = mkIf (kerberos-master || kerberos-slave) {
source-file =
config.fudo.secrets.files.kerberos.realm-principals."${realm}";
target-file = "/run/kdc/realm.principals";
user = krb-user;
group = krb-group;
};
kadmind-keytab = mkIf kerberos-master {
source-file = extractFudoKeytab {
inherit realm;
principals = [ "kadmin/admin" ];
};
target-file = "/run/kdc/kadmind.keytab";
user = krb-user;
group = krb-group;
};
kpasswdd-keytab = mkIf kerberos-master {
source-file = extractFudoKeytab {
inherit realm;
principals = [ "kadmin/changepw" ];
};
target-file = "/run/kdc/kpasswdd.keytab";
user = krb-user;
group = krb-group;
};
hprop-keytab =
mkIf (kerberos-master && (domain.kerberos-slaves != [ ])) {
source-file = extractFudoKeytab {
inherit realm;
principals = [ "kadmin/hprop" ];
};
target-file = "/run/kdc/hprop.keytab";
user = krb-user;
group = krb-group;
};
hpropd-keytab = mkIf kerberos-slave {
source-file = extractFudoHostKeytab {
inherit hostname realm;
services = [ "hprop" ];
};
target-file = "/run/kdc/hpropd.keytab";
user = krb-user;
group = krb-group;
};
};
auth = {
ldap-server = mkIf (ldap-server)
(let
ldap-server = mkIf ldap-server (let
ldap-cert-copy =
config.fudo.acme.host-domains.${hostname}.${cfg.ldap.hostname}.local-copies.openldap;
in {
@ -98,27 +214,28 @@ in {
ssl-ca-certificate = "${pkgs.letsencrypt-ca}";
});
kerberos = {
inherit realm;
kdc = mkIf (kerberos-master || kerberos-slave) {
enable = true;
realm = domain.gssapi-realm;
bind-addresses =
(pkgs.lib.network.host-ips config hostname) ++
[ "127.0.0.1" ] ++ (optional config.networking.enableIPv6 "::1");
state-directory = cfg.kerberos.state-directory;
master-key-file = cfg.kerberos.master-key-file;
master-config = mkIf (kerberos-master) {
master-key-file = host-secrets.realm-master-key.target-file;
primary = mkIf kerberos-master {
enable = true;
acl = let
admin-entries = genAttrs config.instance.local-admins
(admin: {
perms = [ "add" "change-password" "list" ];
});
in admin-entries // {
"*/root".perms = [ "all" ];
adminEntries = genAttrs config.instance.local-admins
(admin: { perms = [ "add" "change-password" "list" ]; });
in adminEntries // { "*/root".perms = [ "all" ]; };
secondary-servers = map getHostFqdn domain.kerberos-slaves;
keytabs = {
kadmind = host-secrets.kadmind-keytab.target-file;
kpasswdd = host-secrets.kpasswdd-keytab.target-file;
hprop = host-secrets.hprop-keytab.target-file;
};
};
slave-config = mkIf (kerberos-slave) {
master-host = domain.kerberos-master;
ipropd-keytab = cfg.kerberos.ipropd-keytab;
secondary = mkIf kerberos-slave {
enable = true;
keytabs.hpropd = host-secrets.hpropd-keytab.target-file;
};
};
};
};
@ -129,19 +246,20 @@ in {
host = hostname;
};
get-fqdn = host:
"${host}.${config.fudo.hosts.${host}.domain}";
get-fqdn = host: "${host}.${config.fudo.hosts.${host}.domain}";
kerberos-master-hosts = optional (kerberized-domain)
domain.kerberos-master;
kerberos-master-hosts =
optional (kerberized-domain) domain.kerberos-master;
kerberos-servers = map get-fqdn
(kerberos-master-hosts ++ domain.kerberos-slaves);
kerberos-servers =
map get-fqdn (kerberos-master-hosts ++ domain.kerberos-slaves);
kerberos-masters = map get-fqdn kerberos-master-hosts;
ldap-servers = map get-fqdn domain.ldap-servers;
in {
gssapi-realm = realm;
srv-records = {
tcp = {
kerberos = map (make-srv-record 88) kerberos-servers;

View File

@ -12,7 +12,7 @@ let
mailserver-domain-name = config.fudo.hosts.${mailserver-host}.domain;
mailserver-domain = config.fudo.domains.${mailserver-domain-name};
mailserver-host-fqdn = "${mailserver-host}.${mailserver-domain-name}";
mailserver-fqdn = "${mailserver-host}.${mailserver-domain-name}";
isMailServer = hostname == mailserver-host;
@ -94,15 +94,12 @@ in {
};
};
zones = mkIf isLocalMailserver {
zones = {
${mailserver-domain.zone} = let
server-ipv4 = pkgs.lib.network.host-ipv4 config mailserver-host;
server-ipv6 = pkgs.lib.network.host-ipv6 config mailserver-host;
srv-record = host: port: [{
host = "${host}.${mailserver-domain-name}";
port = port;
}];
srv-record = host: port: [{ inherit host port; }];
in {
hosts = genAttrs [ "imap" "smtp" ] (alias: {
@ -114,18 +111,17 @@ in {
mx = [ "smtp.${mailserver-domain-name}" ];
aliases =
mkIf metricsEnabled { mail-stats = "${mailserver-host-fqdn}."; };
aliases = mkIf metricsEnabled { mail-stats = "${mailserver-fqdn}."; };
srv-records.tcp = {
pop3 = srv-record "imap" 110;
pop3s = srv-record "imap" 995;
pop3 = srv-record mailserver-fqdn 110;
pop3s = srv-record mailserver-fqdn 995;
imap = srv-record "imap" 143;
imaps = srv-record "imap" 993;
imap = srv-record mailserver-fqdn 143;
imaps = srv-record mailserver-fqdn 993;
smtp = srv-record "smtp" 25;
submission = srv-record "smtp" 587;
smtp = srv-record mailserver-fqdn 25;
submission = srv-record mailserver-fqdn 587;
};
metric-records = mkIf metricsEnabled
@ -167,8 +163,7 @@ in {
ssl-private-key = cert-copy.private-key;
};
local-domains =
[ mailserver-host-fqdn "smtp.${mailserver-domain-name}" ];
local-domains = [ mailserver-fqdn "smtp.${mailserver-domain-name}" ];
mail-directory = "${cfg.state-directory}/mail";
state-directory = "${cfg.state-directory}/state";

164
config/service/mqtt.nix Normal file
View File

@ -0,0 +1,164 @@
{ config, lib, pkgs, ... }@toplevel:
with lib;
let
cfg = config.fudo.services.mqtt;
hostname = config.instance.hostname;
isMqttServer = cfg.host == hostname;
aclOption = with types;
mkOption {
type = listOf str;
description = "Topic filter to which this user has access.";
example = [ "some/topic/#" "other/specific/topic" ];
};
userOpts = { name, ... }: {
options = with types; {
username = mkOption {
type = str;
default = name;
};
password-file = mkOption {
type = str;
description =
"Path to file (on the BUILD HOST) containing the user's password.";
};
acl = aclOption;
};
};
mosquittoUser = config.systemd.services.mosquitto.serviceConfig.User;
pwTarget = type: username: "/run/mqtt/${type}-${username}.passwd";
mqttDomain = config.fudo.hosts."${cfg.host}".domain;
in {
options.fudo.services.mqtt = with types; {
enable = mkEnableOption "Enable MQTT server.";
host = mkOption {
type = str;
description =
"Hostname of the MQTT server for this site/domain/whatever.";
};
listen-address = mkOption {
type = str;
description = "IP address on which to listen.";
default = "0.0.0.0";
};
private = {
enable = mkOption {
type = bool;
description = "Enable a private (authenticated) MQTT server.";
default = true;
};
port = mkOption {
type = port;
description = "Port at which to listen for incoming MQTT requests.";
default = 1883;
};
users = mkOption {
type = attrsOf (submodule userOpts);
default = { };
};
};
public = {
enable = mkEnableOption "Enable a public (anonymous) MQTT server.";
port = mkOption {
type = port;
description = "Port at which to listen for incoming MQTT requests.";
default = 1884;
};
users = mkOption {
type = attrsOf (submodule userOpts);
default = { };
};
acl = aclOption;
};
state-directory = mkOption {
type = str;
description = "Directory where server can store persistent state.";
};
mqtt-hostname = let
mqtt-host = toplevel.config.fudo.services.mqtt.host;
mqtt-domain = toplevel.config.fudo.hosts."${mqtt-host}".domain;
in mkOption {
type = str;
description = "Hostname at which the MQTT server can be reached.";
default = "mqtt.${mqtt-domain}";
};
};
config = mkIf cfg.enable {
networking.firewall.allowedTCPPorts =
(optional cfg.private.enable cfg.private.port)
++ (optional cfg.public.enable cfg.public.port);
systemd = {
services.mosquitto = {
after = [ config.fudo.secrets.secret-target ];
restartIfChanged = true;
};
tmpfiles.rules = optional isMqttServer
"d ${cfg.state-directory} 0700 ${mosquittoUser} - - -";
};
fudo = {
zones."${mqttDomain}".aliases.mqtt = cfg.host;
secrets.host-secrets."${hostname}" = mkIf isMqttServer (let
publicUsers = mapAttrs' (_: userOpts:
nameValuePair "mqtt-public-user-${userOpts.username}" {
source-file = userOpts.password-file;
target-file = pwTarget "public" userOpts.username;
user = mosquittoUser;
}) cfg.public.users;
privateUsers = mapAttrs' (_: userOpts:
nameValuePair "mqtt-private-user-${userOpts.username}" {
source-file = userOpts.password-file;
target-file = pwTarget "private" userOpts.username;
user = mosquittoUser;
}) cfg.private.users;
in publicUsers // privateUsers);
};
services.mosquitto = mkIf isMqttServer {
enable = true;
dataDir = cfg.state-directory;
listeners = (optional cfg.private.enable {
settings.allow_anonymous = false;
port = cfg.private.port;
address = cfg.listen-address;
users = mapAttrs' (_: userOpts:
nameValuePair userOpts.username {
acl = userOpts.acl;
passwordFile = pwTarget "private" userOpts.username;
}) cfg.private.users;
}) ++ (optional cfg.public.enable {
settings.allow_anonymous = true;
acl = map (line: "topic ${line}") cfg.public.acl;
port = cfg.public.port;
address = cfg.listen-address;
users = mapAttrs' (_: userOpts:
nameValuePair userOpts.username {
acl = userOpts.acl;
passwordFile = pwTarget "public" userOpts.username;
}) cfg.public.users;
});
};
};
}

260
config/service/nexus.nix Normal file
View File

@ -0,0 +1,260 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.fudo.services.nexus;
hostname = config.instance.hostname;
domainName = config.fudo.hosts."${hostname}".domain;
domain = config.fudo.domains."${domainName}";
siteName = config.fudo.hosts."${hostname}".site;
site = config.fudo.sites."${siteName}";
hostNexusDomainList = host:
let
domainName = config.fudo.hosts."${host}".domain;
domain = config.fudo.domains."${domainName}";
siteName = config.fudo.hosts."${host}".site;
site = config.fudo.sites."${siteName}";
in unique (domain.nexus.domains ++ site.nexus.domains);
isEmpty = lst: lst == [ ];
localNexusDomains = getAttrs (hostNexusDomainList hostname)
(listKeys config.fudo.nexus.domains);
isServer = let
servers = concatMap (domainOpts: domainOpts.servers)
(attrValues config.fudo.nexus.domains);
in elem hostname servers;
isDnsServer = let
servers = concatMap (domainOpts: domainOpts.dns-servers)
(attrValues config.fudo.nexus.domains);
in elem hostname servers;
isDatabase = hostname == domain.postgresql-server;
enableClient = !isEmpty (hostNexusDomainList hostname);
enable = isServer || isDnsServer || isDatabase || enableClient;
servedDomains = filterAttrs (_: domainOpts:
(elem hostname domainOpts.servers)
|| (elem hostname domainOpts.dns-servers)) config.fudo.nexus.domains;
clientHosts = filter (hostname:
!isEmpty
(intersectLists (hostNexusDomainList hostname) (attrNames servedDomains)))
(attrNames config.fudo.hosts);
hostSecrets = config.fudo.secrets.host-secrets."${hostname}";
hostFqdn = hostname: "${hostname}.${domainName}";
databaseName = "nexus_dns";
serverUser = "nexus_server";
dnsServerUser = "nexus_dns";
concatMapAttrsToList = f: as: concatLists (mapAttrsToList f as);
genServerRecords = name: domain: servers:
imap0 (i: host: {
inherit host domain;
alias = "${name}${toString i}";
}) servers;
serverList = concatMapAttrsToList
(domain: domainOpts: genServerRecords "nexus-" domain domainOpts.servers)
config.fudo.nexus.domains;
dnsServerList = concatMapAttrsToList
(domain: domainOpts: genServerRecords "ns" domain domainOpts.dns-servers)
config.fudo.nexus.domains;
genSshfp = path:
pkgs.stdenv.mkDerivation {
name = "sshfp-${baseNameOf path}";
buildInputs = with pkgs; [ openssh ];
phases = [ "installPhase" ];
installPhase =
"ssh-keygen -r PLACEHOLDER -f ${path} | sed 's/PLACEHOLDER IN SSHFP //' > $out";
};
in {
options.fudo.services.nexus.dns-server = with types; {
listen-addresses = mkOption {
type = nullOr (listOf str);
description =
"Listen addresses. Defaults to 0.0.0.0 (i.e. all addresses).";
default = null;
};
};
config = mkIf enable {
nexus = {
database = {
database = databaseName;
host = pkgs.lib.getDomainPostgresqlServer domainName;
};
domains = mapAttrs (domain: domainOpts: {
admin = "admin@${domain}";
inherit (domainOpts) gssapi-realm;
trusted-networks = domainOpts.trusted-networks
++ config.instance.local-networks;
# aliases = let
# mkAlias = { host, alias, ... }:
# nameValuePair alias (pkgs.lib.getHostFqdn host);
# domainRecords = filter (record: record.domain == domain) serverList;
# in listToAttrs (map mkAlias domainRecords);
nameservers = let
domainNs = filter (record: record.domain == domain) dnsServerList;
mkNsRecord = { alias, host, ... }:
nameValuePair alias {
ipv4-address = pkgs.lib.getHostIpv4 host;
ipv6-address = pkgs.lib.getHostIpv6 host;
};
in listToAttrs (map mkNsRecord domainNs);
records = let
domainServers = filter (record: record.domain == domain) serverList;
mkHostRecords = { host, alias, ... }:
let
ipv4-address = pkgs.lib.getHostIpv4 host;
ipv6-address = pkgs.lib.getHostIpv6 host;
in (optional (ipv4-address != null) {
name = "${alias}.${domain}";
type = "A";
content = ipv4-address;
}) ++ (optional (ipv6-address != null) {
name = "${alias}.${domain}";
type = "AAAA";
content = ipv6-address;
});
in domainOpts.records ++ (concatMap mkHostRecords domainServers);
}) servedDomains;
client = {
enable = enableClient;
inherit hostname;
verbose = true;
domains = unique (domain.nexus.domains ++ site.nexus.domains);
hmac-key-file = hostSecrets.nexus-key.target-file;
servers = let localDomains = hostNexusDomainList hostname;
in map ({ domain, alias, ... }: "${alias}.${domain}")
(filter ({ domain, ... }: elem domain localDomains) serverList);
ssh-key-files = map (key: key.path) config.services.openssh.hostKeys;
};
server = {
enable = isServer;
verbose = true;
client-keys-file = hostSecrets.nexus-client-keys.target-file;
hostnames = let
hostServerRecords =
filter ({ host, ... }: host == hostname) serverList;
in map ({ domain, alias, ... }: "${alias}.${domain}") hostServerRecords;
database = {
user = serverUser;
password-file = hostSecrets.nexus-server-passwd.target-file;
};
};
dns-server = {
enable = isDnsServer;
enable-dnssec = true;
listen-addresses = mkIf (cfg.dns-server.listen-addresses != null)
cfg.dns-server.listen-addresses;
database = {
user = dnsServerUser;
password-file = hostSecrets.nexus-dns-server-passwd.target-file;
};
};
};
fudo = {
secrets.host-secrets."${hostname}" = {
nexus-client-keys = mkIf isServer {
source-file = let
clientKeyFiles =
filterAttrs (hostname: _: elem hostname clientHosts)
config.fudo.secrets.files.nexus-hmacs;
clientKeys =
mapAttrs (_: filename: readFile filename) clientKeyFiles;
in pkgs.writeText "nexus-client-keys.json"
(builtins.toJSON clientKeys);
target-file = "/run/nexus/client-keys.json";
};
nexus-key = mkIf enableClient {
source-file = config.fudo.secrets.files.nexus-hmacs."${hostname}";
target-file = "/run/nexus/client.key";
};
nexus-server-passwd = mkIf isServer {
source-file =
pkgs.lib.passwd.stablerandom-passwd-file "nexus-server-passwd"
"nexus-server-${config.instance.build-seed}";
target-file = "/run/nexus/server-db.passwd";
};
postgres-nexus-server-passwd = mkIf isDatabase {
source-file =
pkgs.lib.passwd.stablerandom-passwd-file "nexus-server-passwd"
"nexus-server-${config.instance.build-seed}";
target-file = "/run/nexus/server-db.passwd";
user = "postgres";
};
nexus-dns-server-passwd = mkIf isDnsServer {
source-file =
pkgs.lib.passwd.stablerandom-passwd-file "nexus-dns-server-passwd"
"nexus-dns-server-${config.instance.build-seed}";
target-file = "/run/nexus/dns-server-db.passwd";
};
postgres-nexus-dns-server-passwd = mkIf isDatabase {
source-file =
pkgs.lib.passwd.stablerandom-passwd-file "nexus-dns-server-passwd"
"nexus-dns-server-${config.instance.build-seed}";
target-file = "/run/nexus-db/nexus-dns.passwd";
user = "postgres";
};
};
postgresql = mkIf isDatabase {
required-services = [ "fudo-passwords.target" ];
databases."${databaseName}".users = config.instance.local-admins;
users = {
"${serverUser}" = {
password-file =
hostSecrets.postgres-nexus-server-passwd.target-file;
databases."${databaseName}" = {
access = "CONNECT";
entity-access = {
"ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE";
"ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE";
};
};
};
"${dnsServerUser}" = {
password-file =
hostSecrets.postgres-nexus-dns-server-passwd.target-file;
databases."${databaseName}" = {
access = "CONNECT";
entity-access = {
"ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE";
"ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE";
};
};
};
};
};
};
};
}

View File

@ -13,8 +13,7 @@ let
host-secrets = config.fudo.secrets.host-secrets.${hostname};
postgresEnabled = domain.postgresql-server == hostname;
publicNetwork = let
site-name = config.fudo.hosts.${hostname}.site;
publicNetwork = let site-name = config.fudo.hosts.${hostname}.site;
in config.fudo.sites.${site-name}.local-gateway == null;
isPostgresHost = hostname == domain.postgresql-server;
@ -32,7 +31,7 @@ in {
};
keytab = mkOption {
type = str;
type = nullOr path;
description = "Keytab for PostgreSQL.";
};
};
@ -49,7 +48,8 @@ in {
};
};
secrets.host-secrets.${hostname}.postgres-keytab = mkIf (cfg.keytab != null) {
secrets.host-secrets.${hostname}.postgres-keytab =
mkIf (cfg.keytab != null) {
source-file = cfg.keytab;
target-file = "/run/postgresql/postgres.keytab";
user = postgresUser;
@ -60,7 +60,8 @@ in {
postgresql = mkIf isPostgresHost (let
ssl-config = optionalAttrs publicNetwork (let
cert-copy = acme-copies.${postgresql-hostname}.local-copies.postgresql;
cert-copy =
acme-copies.${postgresql-hostname}.local-copies.postgresql;
in {
ssl-certificate = mkIf publicNetwork cert-copy.full-certificate;
ssl-private-key = mkIf publicNetwork cert-copy.private-key;
@ -68,7 +69,8 @@ in {
});
in {
enable = true;
keytab = mkIf (cfg.keytab != null) host-secrets.postgres-keytab.target-file;
keytab = mkIf (cfg.keytab != null)
"${host-secrets.postgres-keytab.target-file}";
local-networks = config.instance.local-networks;
state-directory = cfg.state-directory;
required-services = [ config.fudo.secrets.secret-target ];

156
config/service/suanni.nix Normal file
View File

@ -0,0 +1,156 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.fudo.services.suanni;
hostname = config.instance.hostname;
isListener = hostname == cfg.event-listener.host;
isObjectifier = hostname == cfg.objectifier.host;
domain-name = config.fudo.hosts."${hostname}".domain;
host-secrets = config.fudo.secrets.host-secrets."${hostname}";
suanni-mqtt-passwd = pkgs.lib.passwd.stablerandom-passwd-file "suanni-mqtt"
config.instance.build-seed;
in {
options.fudo.services.suanni = with types; {
enable = mkEnableOption "Enable Suan Ni Home Guardian.";
mqtt-topic = mkOption {
type = str;
description = "MQTT topic on which to publish events.";
default = "suanni/events/motion";
};
event-listener = {
host = mkOption {
type = str;
description = "Hostname of Event Listener server.";
};
port = mkOption {
type = port;
default = 5354;
};
};
objectifier = {
host = mkOption {
type = str;
description = "Hostname of objectifier server.";
};
port = mkOption {
type = port;
default = 5121;
};
};
synology = {
host = mkOption {
type = str;
description = "Hostname of the Synology server.";
};
port = mkOption {
type = port;
description = "Port on which to contact the Synology server.";
};
username = mkOption {
type = str;
description = "Username as which to connect to the Synology server.";
};
password-file = mkOption {
type = str;
description = "Path to file containing Synology user password.";
};
};
};
config = mkIf cfg.enable {
services = mkIf (isObjectifier || isListener) {
nginx = {
enable = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedGzipSettings = true;
virtualHosts = {
"event-listener.${domain-name}" = mkIf isListener {
locations."/".proxyPass =
"http://127.0.0.1:${toString cfg.event-listener.port}";
};
"objectifier.${domain-name}" = mkIf isObjectifier {
locations."/".proxyPass =
"http://127.0.0.1:${toString cfg.objectifier.port}";
};
};
};
objectifier = mkIf isObjectifier {
enable = true;
listen-addresses = [ "127.0.0.1" ];
port = cfg.objectifier.port;
};
suanni.server = mkIf isListener {
enable = true;
verbose = true;
event-listener.hostname = "127.0.0.1";
synology-client = {
inherit (cfg.synology) host port username;
password-file = host-secrets.suanni-synology-password.target-file;
};
objectifier-client = {
host = "objectifier.${domain-name}";
port = 80;
};
mqtt-client = {
inherit (config.fudo.services.mqtt.private) port;
host = config.fudo.services.mqtt.mqtt-hostname;
username = "suanni";
password-file = host-secrets.suanni-mqtt-password.target-file;
topic = cfg.mqtt-topic;
};
};
};
fudo = {
secrets.host-secrets."${hostname}" = {
suanni-synology-password = mkIf isListener {
source-file =
config.fudo.secrets.files.service-passwords."${hostname}".suanni-synology;
target-file = "/run/suanni/synology.passwd";
};
suanni-mqtt-password = mkIf isListener {
source-file = suanni-mqtt-passwd;
target-file = "/run/suanni/mqtt.passwd";
};
};
services.mqtt = {
enable = true;
private = {
enable = true;
users.suanni = {
password-file = suanni-mqtt-passwd;
acl = [ "readwrite #" ];
};
};
};
zones."${domain-name}" = {
aliases = {
objectifier = cfg.objectifier.host;
event-listener = "${cfg.event-listener.host}";
};
};
};
};
}

104
config/service/tattler.nix Normal file
View File

@ -0,0 +1,104 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.fudo.services.tattler;
hostname = config.instance.hostname;
isSnooper = config.instance.hostname == cfg.snooper-host;
domain-name = config.fudo.host."${hostname}".domain;
snooper-mqtt-passwd = pkgs.lib.passwd.stablerandom-passwd-file "snooper-mqtt"
config.instance.build-seed;
in {
options.fudo.services.tattler = with types; {
enable = mkEnableOption "Enable Snooper & Tattler notification system.";
verbose = mkEnableOption "Enable verbose output and logging.";
enable-notifications =
mkEnableOption "Enable tattler notifications on the local host.";
event-topics = mkOption {
type = listOf str;
description = "List of MQTT topics on which to listen for motion events.";
};
notification-topic = mkOption {
type = str;
description = "MQTT topic on which to publish notifications.";
default = "fudo/notifications/objects";
};
snooper-host = mkOption {
type = str;
description = "Host on which to run the snooper service.";
};
};
config = mkIf cfg.enable {
fudo = {
secrets.host-secrets."${hostname}" = {
snooper-passwd = mkIf isSnooper {
source-file = snooper-mqtt-passwd;
target-file = "/run/snooper/mqtt.passwd";
};
};
services.mqtt = mkIf isSnooper {
enable = true;
private = {
enable = true;
users.snooper = {
password-file = snooper-mqtt-passwd;
acl = map (topic: "read ${topic}") cfg.event-topics;
};
};
public = {
enable = true;
acl = [ "read ${cfg.notification-topic}" ];
users.snooper = {
password-file = snooper-mqtt-passwd;
acl = [ "readwrite ${cfg.notification-topic}" ];
};
};
};
};
services = {
snooper = mkIf isSnooper {
enable = true;
verbose = true;
event-topics = cfg.event-topics;
notification-topic = cfg.notification-topic;
mqtt = let
host-secrets =
(trace hostname config.fudo.secrets.host-secrets."${hostname}");
in {
incoming = {
port = config.fudo.services.mqtt.private.port;
host = config.fudo.services.mqtt.mqtt-hostname;
username = "snooper";
password-file = host-secrets.snooper-passwd.target-file;
};
outgoing = {
port = config.fudo.services.mqtt.public.port;
host = config.fudo.services.mqtt.mqtt-hostname;
username = "snooper";
password-file = host-secrets.snooper-passwd.target-file;
};
};
};
tattler = mkIf cfg.enable-notifications {
enable = true;
verbose = true;
notification-topic = cfg.notification-topic;
mqtt = {
inherit (config.fudo.services.mqtt.public) port;
host = config.fudo.services.mqtt.mqtt-hostname;
};
};
};
};
}

View File

@ -22,25 +22,6 @@ let
in {
options.fudo.services.wallfly-presence = with types; {
enable = mkEnableOption "Enable WallFly presence for the local site.";
mqtt = {
broker-host = mkOption {
type = str;
description = "Host to serve as local MQTT broker.";
};
port = mkOption {
type = port;
description = "Port on which to listen for MQTT connections.";
default = 1884;
};
listen-address = mkOption {
type = str;
description = "Address on which to listen for MQTT connections.";
default = "0.0.0.0";
};
};
};
config = mkIf cfg.enable {
@ -50,42 +31,30 @@ in {
source-file = userOpts.password-file;
target-file = "/run/wallfly-${username}/passwd";
user = username;
}) local-user-cfg) // (optionalAttrs is-mqtt-broker (mapAttrs'
(username: userOpts:
nameValuePair "wallfly-server-${username}-passwd" {
source-file = userOpts.password-file;
target-file = "/run/wallfly-mqtt/${username}.passwd";
user = config.systemd.services.mosquitto.serviceConfig.User;
}) user-cfg));
zones."${domain-name}" = {
aliases.mqtt = "${mqtt-broker}.${domain-name}.";
};
}) local-user-cfg);
wallfly = {
enable = true;
mqtt = {
broker-uri =
"tcp://${mqtt-broker}.${domain-name}:${toString cfg.mqtt.port}";
mqtt = let
mqtt-hostname = config.fudo.services.mqtt.mqtt-hostname;
mqtt-port = config.fudo.services.mqtt.private.port;
in {
broker-uri = "tcp://${mqtt-hostname}:${toString mqtt-port}";
username = "wallfly-$USER";
password-file = "/run/wallfly-$USER/passwd";
};
};
};
services = {
mosquitto = mkIf (is-mqtt-broker) {
services.mqtt = {
enable = true;
private = {
enable = true;
listeners = [{
settings.allow_anonymous = false;
port = cfg.mqtt.port;
address = cfg.mqtt.listen-address;
users = mapAttrs' (username: userOpts:
nameValuePair "wallfly-${username}" {
passwordFile = "/run/wallfly-mqtt/${username}.passwd";
password-file = userOpts.password-file;
acl = [ "readwrite homeassistant/binary_sensor/#" ];
}) user-cfg;
}];
};
};
};
};

View File

@ -2,7 +2,7 @@
{
imports = [
./service/backplane.nix
# ./service/backplane.nix
./service/chat.nix
./service/chute.nix
./service/dns.nix
@ -12,8 +12,12 @@
./service/logging.nix
./service/mail-server.nix
./service/metrics.nix
./service/mqtt.nix
./service/nexus.nix
./service/postgresql.nix
./service/selby-forum.nix
./service/suanni.nix
./service/tattler.nix
./service/wallfly-presence.nix
# ./service/wireguard-gateway.nix
];

View File

@ -3,9 +3,37 @@
with lib;
let local-domain = "sea.fudo.org";
in {
fudo.services.wallfly-presence = {
fudo.services = {
mqtt = {
enable = true;
mqtt.broker-host = "wormhole0";
host = "wormhole0";
};
wallfly-presence.enable = true;
tattler = let snooper-host = "wormhole0";
in {
enable = true;
verbose = true;
event-topics = [ "suanni/events/motion" ];
inherit snooper-host;
};
suanni = let
listener = "nostromo";
objectifier = "lambda";
in {
enable = true;
event-listener.host = listener;
objectifier.host = objectifier;
synology = {
host = "cargo.sea.fudo.org";
port = 5001;
username = "suanni";
password-file =
config.fudo.secrets.files.service-passwords."${listener}".suanni-synology;
};
};
};
fileSystems = {
@ -165,7 +193,6 @@ in {
DefaultDependencies = false;
ConditionPathExists =
[ "|!/run/gssproxy.pid" "|!/proc/net/rpc/use-gss-proxy" ];
Restart = "always";
};
serviceConfig = {
Type = "forking";

View File

@ -207,7 +207,7 @@
uid = 10065;
primary-group = "fudo";
common-name = "Xiaoxuan Jin";
ldap-hashed-passwd = "{MD5}iecbyMpyVkmOaMBzSFy58Q==";
ldap-hashed-passwd = "{SSHA}04fLLUmqNUpOUJi3IBEja8bFNm0S6W60";
login-hashed-passwd =
"$6$C8lYHrK7KvdKm/RE$cHZ2hg5gEOEjTV8Zoayik8sz5h.Vh0.ClCgOlQn8l/2Qx/qdxqZ7xCsAZ1GZ.IEyESfhJeJbjLpykXDwPpfVF0";
email = "xiaoxuan@fudo.org";

3544
flake.lock

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@
fudo-home = {
url = "git+https://git.fudo.org/fudo-nix/home.git";
# url = "path:/state/fudo-home";
inputs.nixpkgs.follows = "nixpkgs";
};
@ -20,7 +21,10 @@
url = "path:/state/fudo-lib";
};
fudo-pkgs.url = "git+https://git.fudo.org/fudo-nix/pkgs.git";
fudo-pkgs = {
url = "git+https://git.fudo.org/fudo-nix/pkgs.git";
#url = "path:/state/fudo-pkgs";
};
fudo-secrets.url = "path:/state/secrets";
@ -35,11 +39,21 @@
nixpkgs2111.url = "nixpkgs/nixos-21.11";
wallfly.url = "git+https://git.fudo.org/fudo-public/wallfly.git";
objectifier.url = "git+https://git.fudo.org/fudo-public/objectifier.git";
nexus.url = "git+https://git.fudo.org/fudo-public/nexus.git";
suanni.url = "git+https://git.fudo.org/fudo-public/suanni.git";
snooper.url = "git+https://git.fudo.org/fudo-public/snooper.git";
tattler.url = "git+https://git.fudo.org/fudo-public/tattler.git";
};
outputs = { self, nixpkgs, fudo-home, fudo-lib, fudo-entities, fudo-pkgs
, fudo-secrets, chute, chuteUnstable, nixpkgsUnstable, nixpkgs2111, pricebot
, wallfly, ... }@inputs:
, wallfly, objectifier, nexus, suanni, snooper, tattler, ... }@inputs:
with nixpkgs.lib;
let
fudo-nixos-hosts = filterAttrs (hostname: hostOpts: hostOpts.nixos-system)
@ -59,11 +73,14 @@
system = arch;
config = {
allowUnfree = true;
permittedInsecurePackages = [ "openssh-with-gssapi-8.4p1" ];
permittedInsecurePackages =
[ "openssh-with-gssapi-8.4p1" "python3.10-certifi-2022.9.24" ];
};
overlays = [
fudo-lib.overlay
fudo-pkgs.overlay
fudo-pkgs.overlays.default
fudo-secrets.overlays.default
fudo-entities.overlays.default
(final: prev: {
chute = chute.packages.${arch}.chute;
chuteUnstable = chuteUnstable.packages.${arch}.chute;
@ -98,11 +115,19 @@
in { config, ... }: {
imports = [
fudo-home.nixosModules.default
fudo-secrets.nixosModule
fudo-secrets.nixosModules.default
fudo-lib.nixosModule
fudo-entities.nixosModule
pricebot.nixosModules.default
wallfly.nixosModule
objectifier.nixosModules.default
suanni.nixosModules.default
snooper.nixosModules.default
tattler.nixosModules.default
nexus.nixosModules.nexus-client
nexus.nixosModules.nexus-server
nexus.nixosModules.nexus-powerdns
./config
(config-dir + "/hardware/${hostname}.nix")