Added live disk flake file
This commit is contained in:
parent
9c9df8c3c2
commit
2954dfc1b2
|
@ -0,0 +1,7 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./domains/sea.fudo.org.nix
|
||||||
|
];
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
primary-mailserver = fudo.primary-mailserver;
|
||||||
|
|
||||||
|
ldap-servers = fudo.ldap-servers;
|
||||||
|
|
||||||
|
xmpp-servers = fudo.xmpp-servers;
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
{ config, lib, pkgs, modulesPath, ... }:
|
||||||
|
|
||||||
|
with lib; {
|
||||||
|
system.stateVersion = "22.05";
|
||||||
|
|
||||||
|
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
|
||||||
|
|
||||||
|
boot = {
|
||||||
|
initrd = {
|
||||||
|
availableKernelModules = [ "xhci_pci" "usbhid" "usb_storage" ];
|
||||||
|
kernelModules = [ ];
|
||||||
|
};
|
||||||
|
loader = {
|
||||||
|
grub.enable = false;
|
||||||
|
# generic-extlinux-compatible.enable = true;
|
||||||
|
raspberryPi = {
|
||||||
|
enable = true;
|
||||||
|
version = 4;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
tmpOnTmpfs = true;
|
||||||
|
|
||||||
|
kernelModules = [ ];
|
||||||
|
kernelPackages = pkgs.linuxPackages_rpi4;
|
||||||
|
kernelParams = [
|
||||||
|
"8250.nr_uarts=1"
|
||||||
|
"console=ttyAMA0,115200"
|
||||||
|
"console=tty1"
|
||||||
|
];
|
||||||
|
|
||||||
|
extraModulePackages = [ ];
|
||||||
|
};
|
||||||
|
|
||||||
|
hardware = {
|
||||||
|
enableRedistributableFirmware = true;
|
||||||
|
# raspberry-pi."4".fkms-3d.enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems = {
|
||||||
|
"/" = {
|
||||||
|
device = "/dev/disk/by-label/NIXOS_SD";
|
||||||
|
fsType = "ext4";
|
||||||
|
options = [ "noatime" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
"/boot" = {
|
||||||
|
device = "/dev/disk/by-label/FIRMWARE";
|
||||||
|
fsType = "vfat";
|
||||||
|
options = [ "noatime" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
swapDevices = [ ];
|
||||||
|
|
||||||
|
networking = {
|
||||||
|
useDHCP = mkDefault false;
|
||||||
|
|
||||||
|
macvlans = {
|
||||||
|
intif0 = {
|
||||||
|
interface = "eth0";
|
||||||
|
mode = "bridge";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
interfaces = {
|
||||||
|
eth0.useDHCP = false;
|
||||||
|
intif0.macAddress = "02:fa:d4:07:cf:f4";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
powerManagement.cpuFreqGovernor = lib.mkDefault "ondemand";
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,216 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
parent-config = config;
|
||||||
|
|
||||||
|
host-ipv4 = "199.87.154.175";
|
||||||
|
|
||||||
|
local-packages = with pkgs; [
|
||||||
|
bind
|
||||||
|
emacs-nox
|
||||||
|
mtr
|
||||||
|
vim
|
||||||
|
];
|
||||||
|
|
||||||
|
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"
|
||||||
|
config.fudo.zones."selby.ca";
|
||||||
|
|
||||||
|
in {
|
||||||
|
environment.etc = {
|
||||||
|
"generated-zones/fudo.org".text = fudo-zone;
|
||||||
|
"generated-zones/selby.ca".text = selby-zone;
|
||||||
|
};
|
||||||
|
|
||||||
|
fudo = {
|
||||||
|
services.dns.zones = let
|
||||||
|
in {
|
||||||
|
"fudo.org" = {
|
||||||
|
enable = true;
|
||||||
|
external-nameservers = [
|
||||||
|
{
|
||||||
|
ipv4-address = "209.177.102.102";
|
||||||
|
ipv6-address = "2001:470:1f16:40::2";
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ipv4-address = "204.42.254.5";
|
||||||
|
ipv6-address = "2001:418:3f4::5";
|
||||||
|
description = "Nameserver 4, puck.nether.net, in Chicago, IL, US";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
"selby.ca" = {
|
||||||
|
enable = true;
|
||||||
|
external-nameservers = map (n: let
|
||||||
|
i = toString n;
|
||||||
|
in {
|
||||||
|
authoritative-hostname = "ns${i}.fudo.org";
|
||||||
|
description = "Nameserver ${i}, ns${i}.fudo.org.";
|
||||||
|
}) [2 3 4];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
domains."selby.ca" = {
|
||||||
|
local-networks = config.fudo.domains."fudo.org".local-networks;
|
||||||
|
};
|
||||||
|
|
||||||
|
zones = {
|
||||||
|
"fudo.org" = {
|
||||||
|
default-host = host-ipv4;
|
||||||
|
verbatim-dns-records = [
|
||||||
|
# TODO: create these automatically
|
||||||
|
"node._metrics._tcp IN SRV 0 0 443 france.fudo.org."
|
||||||
|
"node._metrics._tcp IN SRV 0 0 9900 hanover.fudo.org."
|
||||||
|
"node._metrics._tcp IN SRV 0 0 443 paris.fudo.org."
|
||||||
|
|
||||||
|
"node._metrics._tcp IN SRV 0 0 443 legatus.fudo.org."
|
||||||
|
"node._metrics._tcp IN SRV 0 0 443 nutboy3.fudo.org."
|
||||||
|
|
||||||
|
"dovecot._metrics._tcp IN SRV 0 0 443 mail.fudo.org."
|
||||||
|
"postfix._metrics._tcp IN SRV 0 0 443 mail.fudo.org."
|
||||||
|
"rspamd._metrics._tcp IN SRV 0 0 443 mail.fudo.org."
|
||||||
|
];
|
||||||
|
};
|
||||||
|
"selby.ca" = {
|
||||||
|
default-host = host-ipv4;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
containers.cashew = {
|
||||||
|
autoStart = true;
|
||||||
|
|
||||||
|
bindMounts = {
|
||||||
|
"/state" = {
|
||||||
|
hostPath = "/state/cashew";
|
||||||
|
isReadOnly = false;
|
||||||
|
};
|
||||||
|
"/etc/bind" = {
|
||||||
|
hostPath = "/state/cashew/bind";
|
||||||
|
isReadOnly = false;
|
||||||
|
};
|
||||||
|
"/var/log" = {
|
||||||
|
hostPath = "/state/cashew/logs";
|
||||||
|
isReadOnly = false;
|
||||||
|
};
|
||||||
|
"/home" = {
|
||||||
|
hostPath = "/state/cashew/home";
|
||||||
|
isReadOnly = false;
|
||||||
|
};
|
||||||
|
"/etc/dns-root-data" = {
|
||||||
|
hostPath = "${pkgs.dns-root-data}/";
|
||||||
|
isReadOnly = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
interfaces = [ "eno2" ];
|
||||||
|
|
||||||
|
config = { config, ... }: {
|
||||||
|
nixpkgs.pkgs = pkgs;
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
systemPackages = local-packages;
|
||||||
|
etc = {
|
||||||
|
"generated-zones/fudo.org" = {
|
||||||
|
text = fudo-zone;
|
||||||
|
};
|
||||||
|
"generated-zones/selby.ca" = {
|
||||||
|
text = selby-zone;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
users = {
|
||||||
|
users = {
|
||||||
|
niten = parent-config.users.users.niten;
|
||||||
|
reaper = parent-config.users.users.reaper // {
|
||||||
|
openssh.authorizedKeys.keys = [
|
||||||
|
"ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADtR1gMK7JnIOht8yZNPROr+0VHgt5eWrGFPscVPk1crVuEvIv1MF544Qk1IHi+2OA2xUvI1BTgmXp3TLvCjEn4lQF4Uc5hcUGENS6TNMPByHx69rAeXVMtmjW0sL4Tbhqd0iNh85STdtzXNZUY31+A6ugrJSnvnSt5wv9ZpMz0SFIE1Q=="
|
||||||
|
];
|
||||||
|
};
|
||||||
|
root.openssh.authorizedKeys.keys = [
|
||||||
|
"ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADtR1gMK7JnIOht8yZNPROr+0VHgt5eWrGFPscVPk1crVuEvIv1MF544Qk1IHi+2OA2xUvI1BTgmXp3TLvCjEn4lQF4Uc5hcUGENS6TNMPByHx69rAeXVMtmjW0sL4Tbhqd0iNh85STdtzXNZUY31+A6ugrJSnvnSt5wv9ZpMz0SFIE1Q=="
|
||||||
|
];
|
||||||
|
};
|
||||||
|
groups = {
|
||||||
|
wheel.members = [
|
||||||
|
"niten"
|
||||||
|
"reaper"
|
||||||
|
];
|
||||||
|
dns = {
|
||||||
|
members = [
|
||||||
|
"niten"
|
||||||
|
"reaper"
|
||||||
|
"named"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking = {
|
||||||
|
defaultGateway = {
|
||||||
|
address = "208.81.4.81";
|
||||||
|
interface = "eno2";
|
||||||
|
};
|
||||||
|
|
||||||
|
interfaces.eno2 = {
|
||||||
|
ipv4.addresses = [
|
||||||
|
{
|
||||||
|
address = "208.81.4.82";
|
||||||
|
prefixLength = 29;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
address = "208.81.1.141";
|
||||||
|
prefixLength = 32;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
firewall.enable = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
# /etc/bind ended up not belonging to the correct user/group
|
||||||
|
systemd.services.bind-perms = {
|
||||||
|
requiredBy = [ "bind.service" ];
|
||||||
|
before = [ "bind.service" ];
|
||||||
|
script = "chown -R named:named /etc/bind";
|
||||||
|
};
|
||||||
|
|
||||||
|
services = {
|
||||||
|
bind = {
|
||||||
|
enable = true;
|
||||||
|
configFile = "/etc/bind/named.conf";
|
||||||
|
};
|
||||||
|
|
||||||
|
openssh = {
|
||||||
|
enable = true;
|
||||||
|
startWhenNeeded = true;
|
||||||
|
useDns = true;
|
||||||
|
permitRootLogin = "prohibit-password";
|
||||||
|
hostKeys = [
|
||||||
|
{
|
||||||
|
path = "/state/ssh/ssh_host_ed25519_key";
|
||||||
|
type = "ed25519";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
path = "/state/ssh/ssh_host_rsa_key";
|
||||||
|
type = "rsa";
|
||||||
|
bits = 4096;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
primary-ip = "10.0.0.3";
|
||||||
|
|
||||||
|
in {
|
||||||
|
networking = {
|
||||||
|
hostName = "wormhole0";
|
||||||
|
|
||||||
|
firewall.enable = false;
|
||||||
|
|
||||||
|
defaultGateway = {
|
||||||
|
address = "10.0.0.1";
|
||||||
|
interface = "intif0";
|
||||||
|
};
|
||||||
|
|
||||||
|
nameservers = [ "10.0.0.1" ];
|
||||||
|
|
||||||
|
interfaces = {
|
||||||
|
intif0 = {
|
||||||
|
ipv4.addresses = [{
|
||||||
|
address = primary-ip;
|
||||||
|
prefixLength = 24;
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
|
||||||
|
wlan0.useDHCP = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nix = {
|
||||||
|
# settings = {
|
||||||
|
# auto-optimise-store = true;
|
||||||
|
# };
|
||||||
|
gc = {
|
||||||
|
automatic = true;
|
||||||
|
dates = "weekly";
|
||||||
|
options = "--delete-older-than 30d";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,188 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
host-fqdn = hostname: "${hostname}.${config.fudo.hosts.${hostname}.domain}";
|
||||||
|
|
||||||
|
postgresql-server = domain.postgresql-server;
|
||||||
|
|
||||||
|
isDatabase = hostname == postgresql-server;
|
||||||
|
isJabber = elem hostname domain.xmpp-servers;
|
||||||
|
isDNSBackplane = hostname == domain.backplane.dns-service;
|
||||||
|
backplaneEnabled = domain.backplane != null;
|
||||||
|
isNameserver = hostname == domain.backplane.nameserver;
|
||||||
|
|
||||||
|
database-name = "backplane_dns";
|
||||||
|
|
||||||
|
make-passwd-file = hostname: let
|
||||||
|
name = "backplane-host-${hostname}-client-passwd";
|
||||||
|
seed = "${name}-${config.instance.build-seed}";
|
||||||
|
in pkgs.lib.passwd.stablerandom-passwd-file name seed;
|
||||||
|
|
||||||
|
host-secrets = config.fudo.secrets.host-secrets.${hostname};
|
||||||
|
|
||||||
|
host-password-files = mapAttrs (hostname: hostOpts:
|
||||||
|
make-passwd-file hostname) config.fudo.hosts;
|
||||||
|
|
||||||
|
backplane-user = "backplane_dns";
|
||||||
|
database-backplane-user = "backplane_dns";
|
||||||
|
database-powerdns-user = "backplane_powerdns_dns";
|
||||||
|
|
||||||
|
backplane-host-domain = config.fudo.hosts.${domain.backplane.dns-service}.domain;
|
||||||
|
backplane-server = head config.fudo.domains.${backplane-host-domain}.xmpp-servers;
|
||||||
|
backplane-host-fqdn = "${backplane-server}.${backplane-host-domain}";
|
||||||
|
backplane-fqdn = "backplane.${backplane-host-domain}";
|
||||||
|
|
||||||
|
|
||||||
|
in {
|
||||||
|
config = mkIf backplaneEnabled {
|
||||||
|
|
||||||
|
fudo = let
|
||||||
|
powerdns-password = pkgs.lib.passwd.stablerandom-passwd-file
|
||||||
|
"backplane-powerdns-passwd-${postgresql-server}"
|
||||||
|
"backplane-powerdns-passwd-${postgresql-server}-${config.instance.build-seed}";
|
||||||
|
backplane-database-password = pkgs.lib.passwd.stablerandom-passwd-file
|
||||||
|
"backplane-passwd-${postgresql-server}"
|
||||||
|
"backplane-passwd-${postgresql-server}-${config.instance.build-seed}";
|
||||||
|
xmpp-password = pkgs.lib.passwd.stablerandom-passwd-file
|
||||||
|
"backplane-xmpp-passwd-${postgresql-server}"
|
||||||
|
"backplane-xmpp-passwd-${postgresql-server}-${config.instance.build-seed}";
|
||||||
|
in {
|
||||||
|
secrets.host-secrets.${hostname} = {
|
||||||
|
powerdns-database-passwd = mkIf isNameserver {
|
||||||
|
source-file = powerdns-password;
|
||||||
|
target-file = "/run/backplane-powerdns/powerdns.passwd";
|
||||||
|
user = config.fudo.powerdns.user;
|
||||||
|
};
|
||||||
|
|
||||||
|
backplane-database-passwd = mkIf isDNSBackplane {
|
||||||
|
source-file = backplane-database-password;
|
||||||
|
target-file = "/run/backplane-dns/database.passwd";
|
||||||
|
user = config.fudo.backplane.dns.user;
|
||||||
|
};
|
||||||
|
backplane-xmpp-passwd = mkIf isDNSBackplane {
|
||||||
|
source-file = xmpp-password;
|
||||||
|
target-file = "/run/backplane-dns/xmpp.passwd";
|
||||||
|
user = config.fudo.backplane.dns.user;
|
||||||
|
};
|
||||||
|
|
||||||
|
database-powerdns-passwd = mkIf isDatabase {
|
||||||
|
source-file = powerdns-password;
|
||||||
|
target-file = "/run/postgres/powerdns.passwd";
|
||||||
|
user = config.services.postgresql.superUser;
|
||||||
|
};
|
||||||
|
database-backplane-passwd = mkIf isDatabase {
|
||||||
|
source-file = backplane-database-password;
|
||||||
|
target-file = "/run/postgres/backplane-database.passwd";
|
||||||
|
user = config.services.postgresql.superUser;
|
||||||
|
};
|
||||||
|
|
||||||
|
ejabberd-backplane-passwd = mkIf isJabber {
|
||||||
|
source-file = xmpp-password;
|
||||||
|
target-file = "/run/backplane-jabber/service-dns.passwd";
|
||||||
|
user = config.services.ejabberd.user;
|
||||||
|
};
|
||||||
|
|
||||||
|
backplane-client-passwd = {
|
||||||
|
source-file = host-password-files.${hostname};
|
||||||
|
target-file = "/run/backplane-client/client.passwd";
|
||||||
|
user = config.fudo.client.dns.user;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
client.dns = {
|
||||||
|
password-file = host-secrets.backplane-client-passwd.target-file;
|
||||||
|
domain = domain.backplane.domain;
|
||||||
|
};
|
||||||
|
|
||||||
|
zones.${zone-name} = {
|
||||||
|
aliases = {
|
||||||
|
backplane = "${backplane-host-fqdn}.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
postgresql = mkIf isDatabase {
|
||||||
|
required-services = [ "fudo-passwords.target" ];
|
||||||
|
|
||||||
|
users = {
|
||||||
|
${database-powerdns-user} = {
|
||||||
|
password-file = host-secrets.database-powerdns-passwd.target-file;
|
||||||
|
databases.${database-name} = {
|
||||||
|
access = "CONNECT";
|
||||||
|
entity-access = {
|
||||||
|
"ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE";
|
||||||
|
"ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
${database-backplane-user} = {
|
||||||
|
password-file = host-secrets.database-backplane-passwd.target-file;
|
||||||
|
databases.${database-name} = {
|
||||||
|
access = "CONNECT";
|
||||||
|
entity-access = {
|
||||||
|
"ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE";
|
||||||
|
"ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
databases.${database-name}.users = config.instance.local-admins;
|
||||||
|
};
|
||||||
|
|
||||||
|
backplane = {
|
||||||
|
enable = isJabber;
|
||||||
|
|
||||||
|
client-hosts = mapAttrs (hostname: hostOpts: {
|
||||||
|
password-file = host-password-files.${hostname};
|
||||||
|
}) config.fudo.hosts;
|
||||||
|
|
||||||
|
services = {
|
||||||
|
dns.password-file = host-secrets.ejabberd-backplane-passwd.source-file;
|
||||||
|
};
|
||||||
|
|
||||||
|
backplane-hostname = backplane-fqdn;
|
||||||
|
|
||||||
|
dns = mkIf isDNSBackplane {
|
||||||
|
enable = true;
|
||||||
|
database = {
|
||||||
|
host = pkgs.lib.network.host-ipv4 config postgresql-server;
|
||||||
|
database = database-name;
|
||||||
|
username = database-backplane-user;
|
||||||
|
password-file = host-secrets.backplane-database-passwd.target-file;
|
||||||
|
};
|
||||||
|
backplane-role = {
|
||||||
|
role = "service-dns";
|
||||||
|
password-file = host-secrets.backplane-xmpp-passwd.target-file;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
powerdns = mkIf (isNameserver) {
|
||||||
|
enable = true;
|
||||||
|
domains = let
|
||||||
|
served-domain = domain.backplane.domain;
|
||||||
|
in {
|
||||||
|
${served-domain}.admin = domain.admin-email;
|
||||||
|
};
|
||||||
|
listen-v4-addresses = let
|
||||||
|
ipv4-addr = pkgs.lib.network.host-ipv4 config hostname;
|
||||||
|
in [ ipv4-addr ];
|
||||||
|
listen-v6-addresses = let
|
||||||
|
ipv6-addr = pkgs.lib.network.host-ipv6 config hostname;
|
||||||
|
in optional (ipv6-addr != null) ipv6-addr;
|
||||||
|
database = {
|
||||||
|
host = pkgs.lib.network.host-ipv4 config postgresql-server;
|
||||||
|
database = database-name;
|
||||||
|
user = database-powerdns-user;
|
||||||
|
password-file = host-secrets.powerdns-database-passwd.target-file;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
hostname = config.instance.hostname;
|
||||||
|
host-secrets = config.fudo.secrets.host-secrets.${hostname};
|
||||||
|
|
||||||
|
cfg = config.informis.services.chute;
|
||||||
|
|
||||||
|
make-env-file = { hostname, secret, passphrase, key, jabber-password ? null }:
|
||||||
|
pkgs.writeText "chute-environment" ''
|
||||||
|
COINBASE_API_HOSTNAME=${hostname}
|
||||||
|
COINBASE_API_SECRET=${secret}
|
||||||
|
COINBASE_API_PASSPHRASE=${passphrase}
|
||||||
|
COINBASE_API_KEY=${key}
|
||||||
|
${optionalString (jabber-password != null)
|
||||||
|
"JABBER_PASSWORD=${jabber-password}"}
|
||||||
|
'';
|
||||||
|
|
||||||
|
read-and-trim = filename:
|
||||||
|
removeSuffix "\n" (readFile filename);
|
||||||
|
|
||||||
|
in {
|
||||||
|
options.informis.services.chute = with types; {
|
||||||
|
enable = mkEnableOption "Enable the Chute cryptocurrency stopgap.";
|
||||||
|
|
||||||
|
jabber-user = mkOption {
|
||||||
|
type = nullOr str;
|
||||||
|
description = "User to which messages will be sent.";
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
staging = {
|
||||||
|
secret-file = mkOption {
|
||||||
|
type = path;
|
||||||
|
description = "Path to file containing Coinbase API secret.";
|
||||||
|
};
|
||||||
|
|
||||||
|
key-file = mkOption {
|
||||||
|
type = path;
|
||||||
|
description = "Path to file containing Coinbase API key.";
|
||||||
|
};
|
||||||
|
|
||||||
|
passphrase-file = mkOption {
|
||||||
|
type = path;
|
||||||
|
description = "Path to file containing Coinbase API passphrase.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = let
|
||||||
|
chute-jabber-passwd =
|
||||||
|
pkgs.lib.passwd.stablerandom-passwd-file
|
||||||
|
"chute-jabber-passwd"
|
||||||
|
"chute-jabber-passwd-${config.instance.build-seed}";
|
||||||
|
in {
|
||||||
|
fudo = {
|
||||||
|
users.chute = {
|
||||||
|
uid = 11007;
|
||||||
|
primary-group = "informis";
|
||||||
|
common-name = "Chute Cryptocurrency Trader";
|
||||||
|
ldap-hashed-passwd =
|
||||||
|
pkgs.lib.passwd.hash-ldap-passwd "chute-jabber-passwd-ldaphash"
|
||||||
|
chute-jabber-passwd;
|
||||||
|
};
|
||||||
|
|
||||||
|
secrets.host-secrets.${hostname}.chute-staging-environment =
|
||||||
|
mkIf cfg.enable {
|
||||||
|
source-file = make-env-file {
|
||||||
|
hostname = "api-public.sandbox.exchange.coinbase.com";
|
||||||
|
secret = read-and-trim cfg.staging.secret-file;
|
||||||
|
passphrase = read-and-trim cfg.staging.passphrase-file;
|
||||||
|
key = read-and-trim cfg.staging.key-file;
|
||||||
|
jabber-password = read-and-trim chute-jabber-passwd;
|
||||||
|
};
|
||||||
|
target-file = "/run/chute/staging.env";
|
||||||
|
user = "root";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
informis.chute = {
|
||||||
|
enable = cfg.enable;
|
||||||
|
stages = {
|
||||||
|
staging = mkIf cfg.enable {
|
||||||
|
package = pkgs.chuteUnstable;
|
||||||
|
environment-file =
|
||||||
|
host-secrets.chute-staging-environment.target-file;
|
||||||
|
jabber = {
|
||||||
|
jid = "chute@jabber.fudo.org";
|
||||||
|
target-jid = cfg.jabber-user;
|
||||||
|
resource = "procul-staging";
|
||||||
|
};
|
||||||
|
currencies = {
|
||||||
|
btc.stop-percentile = 98;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,207 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
hostname = config.instance.hostname;
|
||||||
|
|
||||||
|
domain-name = config.fudo.hosts.${hostname}.domain;
|
||||||
|
site-name = config.fudo.hosts.${hostname}.site;
|
||||||
|
zone-name = config.fudo.domains.${domain-name}.zone;
|
||||||
|
|
||||||
|
site = config.fudo.sites.${site-name};
|
||||||
|
|
||||||
|
cfg = config.fudo.services.local-network;
|
||||||
|
|
||||||
|
resolverOpts = {
|
||||||
|
options = {
|
||||||
|
ip = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "IP address of the upstream recursive resolver.";
|
||||||
|
default = "1.1.1.1";
|
||||||
|
};
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = port;
|
||||||
|
description = "Port of DNS server on the recursive resolver.";
|
||||||
|
default = 53;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
in {
|
||||||
|
options.fudo.services.local-network = with types; {
|
||||||
|
enable = mkEnableOption "Enable local network server.";
|
||||||
|
|
||||||
|
internal-interfaces = mkOption {
|
||||||
|
type = listOf str;
|
||||||
|
description = ''
|
||||||
|
Interfaces on which to:
|
||||||
|
|
||||||
|
* Accept NAT traffic
|
||||||
|
* Serve DNS
|
||||||
|
* Serve DHCP
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
external-interface = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Interface facing the larger internet.";
|
||||||
|
example = "extif0";
|
||||||
|
};
|
||||||
|
|
||||||
|
resolvers = mkOption {
|
||||||
|
type = listOf (submodule resolverOpts);
|
||||||
|
description = "List of upstream DNS servers.";
|
||||||
|
default = [
|
||||||
|
{ ip = "1.1.1.1"; }
|
||||||
|
{ ip = "1.0.0.1"; }
|
||||||
|
{ ip = "9.9.9.9"; }
|
||||||
|
{ ip = "149.112.112.112"; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
dns-filter-proxy = {
|
||||||
|
enable = mkEnableOption "Enable DNS filter.";
|
||||||
|
|
||||||
|
http-listen-port = mkOption {
|
||||||
|
type = port;
|
||||||
|
description = "Port on localhost on which to listen for HTTP requests.";
|
||||||
|
default = 4080;
|
||||||
|
};
|
||||||
|
|
||||||
|
http-host-alias = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Host alias for the DNS filter server.";
|
||||||
|
default = "dns-filter";
|
||||||
|
};
|
||||||
|
|
||||||
|
dns-listen-port = mkOption {
|
||||||
|
type = port;
|
||||||
|
description = "Port on localhost on which to listen for DNS requests.";
|
||||||
|
default = 4053;
|
||||||
|
};
|
||||||
|
|
||||||
|
upstream-dns = mkOption {
|
||||||
|
type = listOf str;
|
||||||
|
description = "List of upstream DNS-over-HTTPS endpoints.";
|
||||||
|
default = [
|
||||||
|
"https://1.1.1.1/dns-query"
|
||||||
|
"https://1.0.0.1/dns-query"
|
||||||
|
# These 11 addrs send the network, so the response can prefer closer answers
|
||||||
|
"https://9.9.9.11/dns-query"
|
||||||
|
"https://149.112.112.11/dns-query"
|
||||||
|
"https://2620:fe::11/dns-query"
|
||||||
|
"https://2620:fe::fe:11/dns-query"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf (site.local-gateway != null) (let
|
||||||
|
host-ipv4 = pkgs.lib.network.host-ipv4 config;
|
||||||
|
gateway-host = site.local-gateway;
|
||||||
|
nameserver-host = gateway-host;
|
||||||
|
gateway-ip = host-ipv4 gateway-host;
|
||||||
|
nameserver-ip = host-ipv4 gateway-host;
|
||||||
|
is-gateway = hostname == gateway-host;
|
||||||
|
agp = cfg.dns-filter-proxy;
|
||||||
|
fqdn = hostname: "${hostname}.${domain-name}.";
|
||||||
|
in {
|
||||||
|
networking = {
|
||||||
|
nat = mkIf is-gateway {
|
||||||
|
enable = true;
|
||||||
|
externalInterface = cfg.external-interface;
|
||||||
|
internalInterfaces = cfg.internal-interfaces;
|
||||||
|
};
|
||||||
|
|
||||||
|
nameservers = [ nameserver-ip ];
|
||||||
|
|
||||||
|
firewall = if is-gateway then {
|
||||||
|
enable = true;
|
||||||
|
trustedInterfaces = cfg.internal-interfaces;
|
||||||
|
} else {
|
||||||
|
enable = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
fudo = {
|
||||||
|
adguard-dns-proxy = mkIf agp.enable {
|
||||||
|
enable = true;
|
||||||
|
http = {
|
||||||
|
listen-ip = "127.0.0.1";
|
||||||
|
listen-port = agp.http-listen-port;
|
||||||
|
};
|
||||||
|
dns = {
|
||||||
|
listen-ips = [ "127.0.0.1" ];
|
||||||
|
listen-port = agp.dns-listen-port;
|
||||||
|
};
|
||||||
|
local-domain-name = domain-name;
|
||||||
|
};
|
||||||
|
|
||||||
|
zones.${zone-name} = {
|
||||||
|
aliases = {
|
||||||
|
${agp.http-host-alias} = optionalAttrs (agp.enable)
|
||||||
|
(fqdn gateway-host);
|
||||||
|
ns = (fqdn nameserver-host);
|
||||||
|
gw = (fqdn gateway-host);
|
||||||
|
};
|
||||||
|
|
||||||
|
hosts = {
|
||||||
|
gateway.ipv4-address = gateway-ip;
|
||||||
|
nameserver.ipv4-address = nameserver-ip;
|
||||||
|
};
|
||||||
|
|
||||||
|
nameservers = [
|
||||||
|
"nameserver"
|
||||||
|
];
|
||||||
|
|
||||||
|
srv-records = {
|
||||||
|
tcp.domain = [{
|
||||||
|
host = "nameserver.${domain-name}";
|
||||||
|
port = 53;
|
||||||
|
}];
|
||||||
|
udp.domain = [{
|
||||||
|
host = "nameserver.${domain-name}";
|
||||||
|
port = 53;
|
||||||
|
}];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
local-network = {
|
||||||
|
enable = is-gateway;
|
||||||
|
domain = domain-name;
|
||||||
|
dns-servers = [ nameserver-ip ];
|
||||||
|
gateway = gateway-ip;
|
||||||
|
dhcp-interfaces = cfg.internal-interfaces;
|
||||||
|
dns-listen-ips = optionals is-gateway [ nameserver-ip "127.0.0.1" "127.0.1.1" ];
|
||||||
|
dns-listen-ipv6s = optionals (is-gateway && config.networking.enableIPv6) [ "::1" ];
|
||||||
|
recursive-resolver = if agp.enable then {
|
||||||
|
host = "127.0.0.1";
|
||||||
|
port = agp.dns-listen-port;
|
||||||
|
} else {
|
||||||
|
host = cfg.resolver.ip;
|
||||||
|
port = cfg.resolver.port;
|
||||||
|
};
|
||||||
|
network = site.network;
|
||||||
|
dhcp-dynamic-network = site.dynamic-network;
|
||||||
|
search-domains = [ domain-name "fudo.org" ];
|
||||||
|
enable-reverse-mappings = true;
|
||||||
|
zone-definition = config.fudo.zones.${zone-name};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.nginx = mkIf agp.enable {
|
||||||
|
enable = true;
|
||||||
|
recommendedGzipSettings = true;
|
||||||
|
recommendedOptimisation = true;
|
||||||
|
recommendedProxySettings = true;
|
||||||
|
|
||||||
|
virtualHosts = {
|
||||||
|
"${agp.http-host-alias}.${domain-name}" = {
|
||||||
|
locations."/".proxyPass =
|
||||||
|
"http://127.0.0.1:${toString agp.http-listen-port}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,231 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
hostname = config.instance.hostname;
|
||||||
|
domain-name = config.fudo.hosts.${hostname}.domain;
|
||||||
|
domain = config.fudo.domains.${domain-name};
|
||||||
|
|
||||||
|
host-fqdn = hostname: let
|
||||||
|
host-domain = config.fudo.hosts.${hostname}.domain;
|
||||||
|
in "${hostname}.${host-domain}";
|
||||||
|
|
||||||
|
logAggregationEnabled = domain.log-aggregator != null;
|
||||||
|
isLogAggregator = hostname == domain.log-aggregator;
|
||||||
|
|
||||||
|
is-private-network = let
|
||||||
|
site-name = config.fudo.hosts.${hostname}.site;
|
||||||
|
in config.fudo.sites.${site-name}.local-gateway != null;
|
||||||
|
|
||||||
|
cfg = config.fudo.services.logging;
|
||||||
|
|
||||||
|
in {
|
||||||
|
options.fudo.services.logging = with types; {
|
||||||
|
loki = {
|
||||||
|
port = mkOption {
|
||||||
|
type = port;
|
||||||
|
description = "Port on which to listen on localhost.";
|
||||||
|
default = 3021;
|
||||||
|
};
|
||||||
|
|
||||||
|
state-directory = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Path at which to store state for Loki.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
promtail = {
|
||||||
|
http-listen-port = mkOption {
|
||||||
|
type = port;
|
||||||
|
default = 6041;
|
||||||
|
};
|
||||||
|
grpc-listen-port = mkOption {
|
||||||
|
type = port;
|
||||||
|
default = 6042;
|
||||||
|
};
|
||||||
|
user = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "User as which to run promtail job.";
|
||||||
|
default = "promtail";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf logAggregationEnabled {
|
||||||
|
users = {
|
||||||
|
users.${cfg.promtail.user} = {
|
||||||
|
isSystemUser = true;
|
||||||
|
uid = 441;
|
||||||
|
group = cfg.promtail.user;
|
||||||
|
};
|
||||||
|
groups = {
|
||||||
|
${cfg.promtail.user}.members = [ cfg.promtail.user ];
|
||||||
|
systemd-journal = {
|
||||||
|
members = [ cfg.promtail.user ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
fudo = let
|
||||||
|
aggregator-domain = config.fudo.hosts.${domain.log-aggregator}.domain;
|
||||||
|
log-aggregator-fqdn = "${domain.log-aggregator}.${aggregator-domain}";
|
||||||
|
in {
|
||||||
|
zones.${domain.zone} = {
|
||||||
|
aliases.log-aggregator = "${log-aggregator-fqdn}.";
|
||||||
|
};
|
||||||
|
|
||||||
|
metrics.grafana.datasources = let
|
||||||
|
scheme = if is-private-network then "http" else "https";
|
||||||
|
in {
|
||||||
|
"loki-${domain.log-aggregator}" = {
|
||||||
|
url = "${scheme}://log-aggregator.${aggregator-domain}";
|
||||||
|
type = "loki";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services = {
|
||||||
|
nginx = mkIf isLogAggregator {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
virtualHosts."log-aggregator.${domain-name}" = {
|
||||||
|
enableACME = ! is-private-network;
|
||||||
|
forceSSL = ! is-private-network;
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://127.0.0.1:${toString cfg.loki.port}";
|
||||||
|
extraConfig = let
|
||||||
|
local-networks = config.instance.local-networks;
|
||||||
|
in "${optionalString ((length local-networks) > 0)
|
||||||
|
(concatStringsSep "\n"
|
||||||
|
(map (network: "allow ${network};") local-networks)) + "\ndeny all;"}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
loki = mkIf isLogAggregator {
|
||||||
|
enable = true;
|
||||||
|
dataDir = cfg.loki.state-directory;
|
||||||
|
# cargo-culted from https://gist.github.com/Xe/c3c786b41ec2820725ee77a7af551225
|
||||||
|
configuration = {
|
||||||
|
auth_enabled = false;
|
||||||
|
|
||||||
|
server.http_listen_port = cfg.loki.port;
|
||||||
|
|
||||||
|
ingester = {
|
||||||
|
lifecycler = {
|
||||||
|
address = "127.0.0.1";
|
||||||
|
ring = {
|
||||||
|
kvstore.store = "inmemory";
|
||||||
|
replication_factor = 1;
|
||||||
|
};
|
||||||
|
final_sleep = "0s";
|
||||||
|
};
|
||||||
|
chunk_idle_period = "1h";
|
||||||
|
max_chunk_age = "1h";
|
||||||
|
chunk_target_size = 10485576;
|
||||||
|
chunk_retain_period = "30s";
|
||||||
|
max_transfer_retries = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
compactor = {
|
||||||
|
working_directory = "${cfg.loki.state-directory}/compactor";
|
||||||
|
};
|
||||||
|
|
||||||
|
schema_config.configs = [
|
||||||
|
{
|
||||||
|
from = "2022-01-01";
|
||||||
|
store = "boltdb-shipper";
|
||||||
|
object_store = "filesystem";
|
||||||
|
schema = "v11";
|
||||||
|
index = {
|
||||||
|
prefix = "index_";
|
||||||
|
period = "24h";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
storage_config = {
|
||||||
|
boltdb_shipper = {
|
||||||
|
active_index_directory = "${cfg.loki.state-directory}/boltdb-shipper/active";
|
||||||
|
cache_location = "${cfg.loki.state-directory}/boltdb-shipper/cache";
|
||||||
|
cache_ttl = "24h";
|
||||||
|
shared_store = "filesystem";
|
||||||
|
};
|
||||||
|
filesystem.directory = "${cfg.loki.state-directory}/chunks";
|
||||||
|
};
|
||||||
|
|
||||||
|
limits_config = {
|
||||||
|
reject_old_samples = true;
|
||||||
|
reject_old_samples_max_age = "168h";
|
||||||
|
};
|
||||||
|
|
||||||
|
chunk_store_config.max_look_back_period = "0s";
|
||||||
|
|
||||||
|
table_manager = {
|
||||||
|
retention_deletes_enabled = false;
|
||||||
|
retention_period = "0s";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd = {
|
||||||
|
tmpfiles.rules = mkIf isLogAggregator (let
|
||||||
|
user = config.services.loki.user;
|
||||||
|
statedir = cfg.loki.state-directory;
|
||||||
|
in [
|
||||||
|
"d ${statedir} 0700 ${user} - - -"
|
||||||
|
"d ${statedir}/boltdb-shipper 0700 ${user} - - -"
|
||||||
|
"d ${statedir}/boltdb-shipper/active 0700 ${user} - - -"
|
||||||
|
"d ${statedir}/boltdb-shipper/cache 0700 ${user} - - -"
|
||||||
|
"d ${statedir}/chunks 0700 ${user} - - -"
|
||||||
|
"d ${statedir}/compactor 0700 ${user} - - -"
|
||||||
|
]);
|
||||||
|
|
||||||
|
services.promtail = let
|
||||||
|
scheme = if is-private-network then "http" else "https";
|
||||||
|
|
||||||
|
loki-host = host-fqdn domain.log-aggregator;
|
||||||
|
|
||||||
|
config = builtins.toJSON {
|
||||||
|
server = {
|
||||||
|
http_listen_port = cfg.promtail.http-listen-port;
|
||||||
|
grpc_listen_port = cfg.promtail.grpc-listen-port;
|
||||||
|
};
|
||||||
|
positions.filename = "/tmp/positions.yml";
|
||||||
|
clients = [
|
||||||
|
{ url = "${scheme}://log-aggregator.${domain-name}/loki/api/v1/push"; }
|
||||||
|
];
|
||||||
|
scrape_configs = [
|
||||||
|
{
|
||||||
|
job_name = "journal";
|
||||||
|
journal = {
|
||||||
|
max_age = "12h";
|
||||||
|
labels = {
|
||||||
|
job = "systemd-journal";
|
||||||
|
host = hostname;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
relabel_configs = [{
|
||||||
|
source_labels = [ "__journal__systemd_unit" ];
|
||||||
|
target_label = "unit";
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
config-file = pkgs.writeText "promtail-config.json" config;
|
||||||
|
in {
|
||||||
|
description = "PromTail log aggregator client for Loki.";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = ''
|
||||||
|
${pkgs.grafana-loki}/bin/promtail --config.file ${config-file}
|
||||||
|
'';
|
||||||
|
User = cfg.promtail.user;
|
||||||
|
PrivateTmp = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,180 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
cfg = config.fudo.services.mail-server;
|
||||||
|
|
||||||
|
hostname = config.instance.hostname;
|
||||||
|
domain-name = config.instance.local-domain;
|
||||||
|
domain = config.fudo.domains.${domain-name};
|
||||||
|
|
||||||
|
mailserver-host = domain.primary-mailserver;
|
||||||
|
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}";
|
||||||
|
|
||||||
|
isMailServer = hostname == mailserver-host;
|
||||||
|
|
||||||
|
isLocalMailserver = domain-name == mailserver-domain-name;
|
||||||
|
|
||||||
|
metricsEnabled = mailserver-domain.prometheus-hosts != [];
|
||||||
|
|
||||||
|
host-certs = config.fudo.acme.host-domains.${hostname};
|
||||||
|
|
||||||
|
in {
|
||||||
|
options.fudo.services.mail-server = with types; {
|
||||||
|
debug = mkEnableOption "Enable debug options for mailserver.";
|
||||||
|
|
||||||
|
state-directory = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Directory at which to store mailserver state.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
services.nginx = mkIf (isMailServer && metricsEnabled) {
|
||||||
|
enable = true;
|
||||||
|
recommendedOptimisation = true;
|
||||||
|
recommendedProxySettings = true;
|
||||||
|
|
||||||
|
virtualHosts."mail-stats.${mailserver-domain-name}" = let
|
||||||
|
trusted-networks = config.instance.local-networks;
|
||||||
|
trustedNetworkString = optionalString (length trusted-networks > 0)
|
||||||
|
(concatStringsSep "\n"
|
||||||
|
(map (network: "allow ${network};")
|
||||||
|
trusted-networks)) + "\n\ndeny all;";
|
||||||
|
in {
|
||||||
|
enableACME = true;
|
||||||
|
forceSSL = true;
|
||||||
|
|
||||||
|
locations = let
|
||||||
|
monitor-cfg = config.fudo.mail-server.monitoring;
|
||||||
|
in {
|
||||||
|
"/metrics/dovecot" = {
|
||||||
|
proxyPass = "http://127.0.0.1:${toString monitor-cfg.dovecot-listen-port}/metrics";
|
||||||
|
extraConfig = trustedNetworkString;
|
||||||
|
};
|
||||||
|
"/metrics/postfix" = {
|
||||||
|
proxyPass = "http://127.0.0.1:${toString monitor-cfg.postfix-listen-port}/metrics";
|
||||||
|
extraConfig = trustedNetworkString;
|
||||||
|
};
|
||||||
|
"/metrics/rspamd" = {
|
||||||
|
proxyPass = "http://127.0.0.1:${toString monitor-cfg.rspamd-listen-port}/metrics";
|
||||||
|
extraConfig = trustedNetworkString;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
fudo = {
|
||||||
|
acme.host-domains = mkIf isMailServer {
|
||||||
|
${hostname} = {
|
||||||
|
"imap.${mailserver-domain-name}" = {
|
||||||
|
admin-email = "admin@${mailserver-domain-name}";
|
||||||
|
local-copies.dovecot = {
|
||||||
|
user = config.services.dovecot2.user;
|
||||||
|
dependent-services = [ "dovecot2.services" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
"smtp.${mailserver-domain-name}" = {
|
||||||
|
admin-email = "admin@${mailserver-domain-name}";
|
||||||
|
local-copies.postfix = {
|
||||||
|
user = config.services.postfix.user;
|
||||||
|
dependent-services = [ "postfix.services" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
zones = mkIf isLocalMailserver {
|
||||||
|
${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;
|
||||||
|
}];
|
||||||
|
|
||||||
|
in {
|
||||||
|
hosts = genAttrs [ "imap" "smtp" ] (alias: {
|
||||||
|
ipv4-address = server-ipv4;
|
||||||
|
ipv6-address = server-ipv6;
|
||||||
|
description = "Primary ${toUpper alias} server for ${mailserver-domain-name}.";
|
||||||
|
});
|
||||||
|
|
||||||
|
mx = [ "smtp.${mailserver-domain-name}" ];
|
||||||
|
|
||||||
|
aliases = mkIf metricsEnabled {
|
||||||
|
mail-stats = "${mailserver-host-fqdn}.";
|
||||||
|
};
|
||||||
|
|
||||||
|
srv-records.tcp = {
|
||||||
|
pop3 = srv-record "imap" 110;
|
||||||
|
pop3s = srv-record "imap" 995;
|
||||||
|
|
||||||
|
imap = srv-record "imap" 143;
|
||||||
|
imaps = srv-record "imap" 993;
|
||||||
|
|
||||||
|
smtp = srv-record "smtp" 25;
|
||||||
|
submission = srv-record "smtp" 587;
|
||||||
|
};
|
||||||
|
|
||||||
|
metric-records = mkIf metricsEnabled
|
||||||
|
(genAttrs [ "dovecot" "postfix" "rspamd" ]
|
||||||
|
(_: srv-record "mail-stats" 443));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
metrics.prometheus.service-discovery-dns = mkIf metricsEnabled
|
||||||
|
(genAttrs [ "dovecot" "postfix" "rspamd" ]
|
||||||
|
(mtype: [ "${mtype}._metrics._tcp.${mailserver-domain-name}" ]));
|
||||||
|
|
||||||
|
mail-server = mkIf isLocalMailserver {
|
||||||
|
enable = isMailServer;
|
||||||
|
|
||||||
|
domain = mailserver-domain-name;
|
||||||
|
mail-hostname = "smtp.${mailserver-domain-name}";
|
||||||
|
monitoring.enable = metricsEnabled;
|
||||||
|
|
||||||
|
debug = cfg.debug;
|
||||||
|
|
||||||
|
clamav.enable = true;
|
||||||
|
|
||||||
|
dkim.signing = true;
|
||||||
|
|
||||||
|
dovecot = let
|
||||||
|
cert-copy = host-certs."imap.${mailserver-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.${mailserver-domain-name}".local-copies.postfix;
|
||||||
|
in {
|
||||||
|
ssl-certificate = cert-copy.full-certificate;
|
||||||
|
ssl-private-key = cert-copy.private-key;
|
||||||
|
};
|
||||||
|
|
||||||
|
local-domains = [ mailserver-host-fqdn "smtp.${mailserver-domain-name}" ];
|
||||||
|
|
||||||
|
mail-directory = "${cfg.state-directory}/mail";
|
||||||
|
state-directory = "${cfg.state-directory}/state";
|
||||||
|
|
||||||
|
trusted-networks = config.instance.local-networks;
|
||||||
|
|
||||||
|
alias-users = genAttrs [
|
||||||
|
"root"
|
||||||
|
"postmaster"
|
||||||
|
"hostmaster"
|
||||||
|
"webmaster"
|
||||||
|
"system"
|
||||||
|
"admin"
|
||||||
|
"dmarc-report"
|
||||||
|
] (alias: config.instance.local-admins);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,311 @@
|
||||||
|
{ config, lib, pkgs, ... }@toplevel:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
hostname = config.instance.hostname;
|
||||||
|
domain-name = config.fudo.hosts.${hostname}.domain;
|
||||||
|
domain = config.fudo.domains.${domain-name};
|
||||||
|
|
||||||
|
pthru = obj: builtins.trace "TRACE(${hostname}): ${toString obj}" obj;
|
||||||
|
|
||||||
|
host-secrets = config.fudo.secrets.host-secrets.${hostname};
|
||||||
|
|
||||||
|
notEmpty = lst: (length lst) > 0;
|
||||||
|
|
||||||
|
metricsEnabled = notEmpty domain.prometheus-hosts;
|
||||||
|
metricsScraper = elem hostname domain.prometheus-hosts;
|
||||||
|
metricsMonitor = elem hostname domain.grafana-hosts;
|
||||||
|
|
||||||
|
prometheus-cfg = config.fudo.services.metrics.prometheus;
|
||||||
|
grafana-cfg = config.fudo.services.metrics.grafana;
|
||||||
|
|
||||||
|
host-fqdn = hostname:
|
||||||
|
let host-domain = config.fudo.hosts.${hostname}.domain;
|
||||||
|
in "${hostname}.${host-domain}";
|
||||||
|
|
||||||
|
host-auth-fqdn = hostname: "${host-fqdn hostname}.";
|
||||||
|
|
||||||
|
make-alias-map = type: hosts:
|
||||||
|
listToAttrs
|
||||||
|
(imap0 (i: hostname: nameValuePair hostname "${type}-${toString i}") hosts);
|
||||||
|
|
||||||
|
headOrNull = lst: if notEmpty lst then head lst else null;
|
||||||
|
|
||||||
|
metrics-master = headOrNull domain.prometheus-hosts;
|
||||||
|
|
||||||
|
monitor-master = headOrNull domain.grafana-hosts;
|
||||||
|
|
||||||
|
metrics-alias-map = make-alias-map "metrics" domain.prometheus-hosts;
|
||||||
|
|
||||||
|
monitor-alias-map = make-alias-map "monitor" domain.grafana-hosts;
|
||||||
|
|
||||||
|
alias-map-to-cnames =
|
||||||
|
mapAttrs' (hostname: alias: nameValuePair alias (host-auth-fqdn hostname));
|
||||||
|
|
||||||
|
alias-map-to-hostnames =
|
||||||
|
mapAttrsToList (hostname: alias: "${alias}.${domain-name}");
|
||||||
|
|
||||||
|
grafana-smtp-password-file =
|
||||||
|
pkgs.lib.passwd.stablerandom-passwd-file "grafana-smtp-passwd"
|
||||||
|
"grafana-smtp-passwd-${config.instance.build-seed}";
|
||||||
|
|
||||||
|
grafana-auth-password-file =
|
||||||
|
pkgs.lib.passwd.stablerandom-passwd-file "grafana-auth-passwd"
|
||||||
|
"grafana-auth-passwd-${config.instance.build-seed}";
|
||||||
|
|
||||||
|
grafana-admin-password-file =
|
||||||
|
pkgs.lib.passwd.stablerandom-passwd-file "grafana-admin-passwd"
|
||||||
|
"grafana-admin-passwd-${config.instance.build-seed}";
|
||||||
|
|
||||||
|
grafana-secret-key-file =
|
||||||
|
pkgs.lib.passwd.stablerandom-passwd-file "grafana-secret-key"
|
||||||
|
"grafana-secret-key-${config.instance.build-seed}";
|
||||||
|
|
||||||
|
is-private-network = let site-name = config.fudo.hosts.${hostname}.site;
|
||||||
|
in config.fudo.sites.${site-name}.local-gateway != null;
|
||||||
|
|
||||||
|
domainToBaseDn = domain:
|
||||||
|
concatStringsSep "," (map (el: "dc=${el}") (splitString "." domain));
|
||||||
|
|
||||||
|
ldapEnabled = domain.ldap-servers != [ ];
|
||||||
|
|
||||||
|
in {
|
||||||
|
options.fudo.services.metrics = with types; {
|
||||||
|
prometheus = {
|
||||||
|
static-targets = mkOption {
|
||||||
|
type = attrsOf (listOf str);
|
||||||
|
description =
|
||||||
|
"A map of exporter type to a list of host:ports from which to collect metrics.";
|
||||||
|
example = { dovecot = [ "my.host.name:1111" ]; };
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
state-directory = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Path at which to store Prometheus state.";
|
||||||
|
default = "/var/lib/prometheus";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
grafana = {
|
||||||
|
smtp = {
|
||||||
|
username = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Username from which to send Grafana alerts.";
|
||||||
|
default = "monitor";
|
||||||
|
};
|
||||||
|
hostname = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Hostname of the SMTP host.";
|
||||||
|
default = "mail.${toplevel.config.instance.local-domain}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
ldap = let base-dn = domainToBaseDn config.instance.local-domain;
|
||||||
|
in {
|
||||||
|
base-dn = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "DN under which to search for users.";
|
||||||
|
default = base-dn;
|
||||||
|
};
|
||||||
|
|
||||||
|
bind-user = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "DN as which to bind to the LDAP server.";
|
||||||
|
default = "grafana_reader";
|
||||||
|
};
|
||||||
|
|
||||||
|
bind-passwd = mkOption {
|
||||||
|
type = nullOr str;
|
||||||
|
description = "Path to file with bind password. Generated if null.";
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
database = {
|
||||||
|
hostname = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Hostname of the postgresql database.";
|
||||||
|
default = "localhost";
|
||||||
|
};
|
||||||
|
user = mkOption {
|
||||||
|
type = str;
|
||||||
|
description =
|
||||||
|
"Username as which to authenticate to the postgresql database.";
|
||||||
|
};
|
||||||
|
password-file = mkOption {
|
||||||
|
type = str;
|
||||||
|
description =
|
||||||
|
"Password file (on the target host) which to authenticate to the postgresql database.";
|
||||||
|
};
|
||||||
|
name = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Database name.";
|
||||||
|
default = "grafana";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
state-directory = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Path at which to store Grafana state.";
|
||||||
|
default = "/var/lib/grafana";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf metricsEnabled {
|
||||||
|
fudo = {
|
||||||
|
system-users = {
|
||||||
|
${grafana-cfg.smtp.username} = {
|
||||||
|
description = "Grafana Alerts";
|
||||||
|
ldap-hashed-password =
|
||||||
|
pkgs.lib.passwd.hash-ldap-passwd "grafana-smtp-passwd"
|
||||||
|
grafana-smtp-password-file;
|
||||||
|
};
|
||||||
|
|
||||||
|
${grafana-cfg.ldap.bind-user} = mkIf ((domain.ldap-servers != [ ])
|
||||||
|
&& (grafana-cfg.ldap.bind-passwd == null)) {
|
||||||
|
description = "Grafana Authentication Reader";
|
||||||
|
ldap-hashed-password =
|
||||||
|
pkgs.lib.passwd.hash-ldap-passwd "grafana-auth-passwd"
|
||||||
|
grafana-auth-password-file;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
secrets.host-secrets = mkIf metricsMonitor
|
||||||
|
(let grafana-user = config.systemd.services.grafana.serviceConfig.User;
|
||||||
|
in {
|
||||||
|
${hostname} = {
|
||||||
|
grafana-smtp-password = {
|
||||||
|
source-file = grafana-smtp-password-file;
|
||||||
|
target-file = "/run/metrics/grafana/smtp.passwd";
|
||||||
|
user = grafana-user;
|
||||||
|
};
|
||||||
|
|
||||||
|
grafana-admin-password = {
|
||||||
|
source-file = grafana-admin-password-file;
|
||||||
|
target-file = "/run/metrics/grafana/admin.passwd";
|
||||||
|
user = grafana-user;
|
||||||
|
};
|
||||||
|
|
||||||
|
grafana-secret-key = {
|
||||||
|
source-file = grafana-secret-key-file;
|
||||||
|
target-file = "/run/metrics/grafana/secret.key";
|
||||||
|
user = grafana-user;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
zones.${domain.zone} = {
|
||||||
|
aliases = let
|
||||||
|
metrics-aliases = alias-map-to-cnames metrics-alias-map;
|
||||||
|
monitor-aliases = alias-map-to-cnames monitor-alias-map;
|
||||||
|
metrics-master-cname = optionalAttrs (metrics-master != null) {
|
||||||
|
metrics = "${metrics-master}.${domain-name}.";
|
||||||
|
};
|
||||||
|
monitor-master-cname = optionalAttrs (monitor-master != null) {
|
||||||
|
monitor = "${monitor-master}.${domain-name}.";
|
||||||
|
};
|
||||||
|
in metrics-aliases // monitor-aliases // metrics-master-cname
|
||||||
|
// monitor-master-cname;
|
||||||
|
|
||||||
|
metric-records = let
|
||||||
|
domain-hosts = filterAttrs (hostname: hostOpts:
|
||||||
|
hostOpts.domain == domain-name && hostOpts.nixos-system)
|
||||||
|
config.fudo.hosts;
|
||||||
|
in {
|
||||||
|
node = map (hostname: {
|
||||||
|
host = "${hostname}.${domain-name}";
|
||||||
|
port = if is-private-network then 80 else 443;
|
||||||
|
}) (attrNames domain-hosts);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
metrics = {
|
||||||
|
node-exporter = {
|
||||||
|
enable = true;
|
||||||
|
hostname = host-fqdn hostname;
|
||||||
|
private-network = is-private-network;
|
||||||
|
};
|
||||||
|
|
||||||
|
prometheus = mkIf metricsScraper {
|
||||||
|
enable = true;
|
||||||
|
service-discovery-dns = {
|
||||||
|
node = [ "node._metrics._tcp.${domain-name}" ];
|
||||||
|
};
|
||||||
|
static-targets = prometheus-cfg.static-targets;
|
||||||
|
hostname = let alias = metrics-alias-map.${hostname};
|
||||||
|
in "${alias}.${domain-name}";
|
||||||
|
state-directory = prometheus-cfg.state-directory;
|
||||||
|
private-network = is-private-network;
|
||||||
|
};
|
||||||
|
|
||||||
|
grafana = mkIf metricsMonitor {
|
||||||
|
enable = true;
|
||||||
|
hostname = let alias = monitor-alias-map.${hostname};
|
||||||
|
in "${alias}.${domain-name}";
|
||||||
|
smtp = let cfg = grafana-cfg.smtp;
|
||||||
|
in {
|
||||||
|
username = cfg.username;
|
||||||
|
password-file = host-secrets.grafana-smtp-password.target-file;
|
||||||
|
hostname = cfg.hostname;
|
||||||
|
email = "${cfg.username}@${domain-name}";
|
||||||
|
};
|
||||||
|
database = let cfg = grafana-cfg.database;
|
||||||
|
in {
|
||||||
|
name = cfg.name;
|
||||||
|
user = cfg.user;
|
||||||
|
password-file = cfg.password-file;
|
||||||
|
hostname = cfg.hostname;
|
||||||
|
};
|
||||||
|
ldap = mkIf (domain.ldap-servers != [ ]) {
|
||||||
|
hosts = map host-fqdn domain.ldap-servers;
|
||||||
|
base-dn = grafana-cfg.ldap.base-dn;
|
||||||
|
bind-dn =
|
||||||
|
"cn=${grafana-cfg.ldap.bind-user},${grafana-cfg.ldap.base-dn}";
|
||||||
|
bind-passwd = if (grafana-cfg.ldap.bind-passwd != null) then
|
||||||
|
grafana-cfg.ldap.bind-passwd
|
||||||
|
else
|
||||||
|
(readFile grafana-auth-password-file);
|
||||||
|
};
|
||||||
|
admin-password-file = host-secrets.grafana-admin-password.target-file;
|
||||||
|
secret-key-file = host-secrets.grafana-secret-key.target-file;
|
||||||
|
datasources = let
|
||||||
|
scheme = if is-private-network then "http" else "https";
|
||||||
|
host-config = hostname: {
|
||||||
|
url = "${scheme}://${hostname}.${domain-name}";
|
||||||
|
type = "prometheus";
|
||||||
|
default = hostname == "metrics-0";
|
||||||
|
};
|
||||||
|
in listToAttrs
|
||||||
|
(map (host: nameValuePair "prometheus-${host}" (host-config host))
|
||||||
|
(attrValues metrics-alias-map));
|
||||||
|
state-directory = grafana-cfg.state-directory;
|
||||||
|
private-network = is-private-network;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.nginx =
|
||||||
|
mkIf (hostname == metrics-master || hostname == monitor-master) {
|
||||||
|
enable = true;
|
||||||
|
recommendedOptimisation = true;
|
||||||
|
recommendedProxySettings = true;
|
||||||
|
|
||||||
|
virtualHosts =
|
||||||
|
let scheme = if is-private-network then "http" else "https";
|
||||||
|
in {
|
||||||
|
"metrics.${domain-name}" = mkIf (hostname == metrics-master) {
|
||||||
|
enableACME = !is-private-network;
|
||||||
|
forceSSL = !is-private-network;
|
||||||
|
locations."/".return = let alias = metrics-alias-map.${hostname};
|
||||||
|
in "301 ${scheme}://${alias}.${domain-name}$request_uri";
|
||||||
|
};
|
||||||
|
"monitor.${domain-name}" = mkIf (hostname == monitor-master) {
|
||||||
|
enableACME = !is-private-network;
|
||||||
|
forceSSL = !is-private-network;
|
||||||
|
locations."/".return = let alias = monitor-alias-map.${hostname};
|
||||||
|
in "301 ${scheme}://${alias}.${domain-name}$request_uri";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
hostname = config.instance.hostname;
|
||||||
|
domain-name = config.fudo.hosts.${hostname}.domain;
|
||||||
|
domain = config.fudo.domains.${domain-name};
|
||||||
|
|
||||||
|
cfg = config.fudo.services.postgresql;
|
||||||
|
|
||||||
|
zone-name = domain.zone;
|
||||||
|
|
||||||
|
host-secrets = config.fudo.secrets.host-secrets.${hostname};
|
||||||
|
|
||||||
|
postgresEnabled = domain.postgresql-server == hostname;
|
||||||
|
publicNetwork = let
|
||||||
|
site-name = config.fudo.hosts.${hostname}.site;
|
||||||
|
in config.fudo.sites.${site-name}.local-gateway == null;
|
||||||
|
isPostgresHost = hostname == domain.postgresql-server;
|
||||||
|
|
||||||
|
postgresql-hostname = "postgresql.${domain-name}";
|
||||||
|
|
||||||
|
acme-copies = config.fudo.acme.host-domains.${hostname};
|
||||||
|
|
||||||
|
postgresUser = config.systemd.services.postgresql.serviceConfig.User;
|
||||||
|
|
||||||
|
in {
|
||||||
|
options.fudo.services.postgresql = with types; {
|
||||||
|
state-directory = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Path at which to store PostgreSQL state.";
|
||||||
|
};
|
||||||
|
|
||||||
|
keytab = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Keytab for PostgreSQL.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf postgresEnabled {
|
||||||
|
fudo = {
|
||||||
|
acme.host-domains.${hostname} = mkIf (publicNetwork && isPostgresHost) {
|
||||||
|
${postgresql-hostname}.local-copies = {
|
||||||
|
postgresql = {
|
||||||
|
user = postgresUser;
|
||||||
|
dependent-services = [ "postgresql.service" ];
|
||||||
|
part-of = [ config.fudo.postgresql.systemd-target ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
secrets.host-secrets.${hostname}.postgres-keytab = mkIf (cfg.keytab != null) {
|
||||||
|
source-file = cfg.keytab;
|
||||||
|
target-file = "/run/postgresql/postgres.keytab";
|
||||||
|
user = postgresUser;
|
||||||
|
};
|
||||||
|
|
||||||
|
zones.${zone-name}.aliases.postgresql =
|
||||||
|
"${domain.postgresql-server}.${domain-name}.";
|
||||||
|
|
||||||
|
postgresql = mkIf isPostgresHost (let
|
||||||
|
ssl-config = optionalAttrs publicNetwork (let
|
||||||
|
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;
|
||||||
|
required-services = [ cert-copy.service ];
|
||||||
|
});
|
||||||
|
in {
|
||||||
|
enable = true;
|
||||||
|
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 ];
|
||||||
|
} // ssl-config);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,277 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
relative-hostname = "forum.test";
|
||||||
|
|
||||||
|
strip-hash = filename: head (builtins.match "^[a-zA-Z0-9]+-(.+)$" filename);
|
||||||
|
|
||||||
|
clean-utf8-file = filename:
|
||||||
|
pkgs.stdenv.mkDerivation {
|
||||||
|
name = "${strip-hash (baseNameOf filename)}.utf8";
|
||||||
|
|
||||||
|
phases = [ "installPhase" ];
|
||||||
|
|
||||||
|
installPhase = "iconv -c -f utf-8 -t utf-8 -o $out ${filename}";
|
||||||
|
};
|
||||||
|
|
||||||
|
in {
|
||||||
|
options.fudo.services.selby-forum = with types; {
|
||||||
|
enable = mkEnableOption "Enable Selby Forum on this host.";
|
||||||
|
|
||||||
|
state-directory = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Directory at which to store Selby Forum state.";
|
||||||
|
};
|
||||||
|
|
||||||
|
legacy-forum-data = mkOption {
|
||||||
|
type = path;
|
||||||
|
description = "Path to legacy Vanilla forum data.";
|
||||||
|
};
|
||||||
|
|
||||||
|
external-interface = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Public-facing network interface on this host.";
|
||||||
|
};
|
||||||
|
|
||||||
|
mail = {
|
||||||
|
host = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Mail server hostname.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = (let
|
||||||
|
hostname = config.instance.hostname;
|
||||||
|
|
||||||
|
cfg = config.fudo.services.selby-forum;
|
||||||
|
|
||||||
|
host-fqdn = let host-domain = config.fudo.hosts.${hostname}.domain;
|
||||||
|
in "${hostname}.${host-domain}";
|
||||||
|
|
||||||
|
host-secrets = config.fudo.secrets.host-secrets.${hostname};
|
||||||
|
|
||||||
|
parent-ip = "192.168.92.1";
|
||||||
|
child-ip = "192.168.92.2";
|
||||||
|
|
||||||
|
mkPasswd = name:
|
||||||
|
pkgs.lib.passwd.stablerandom-passwd-file "selby-forum-${name}"
|
||||||
|
"selby-forum-${name}-${config.instance.build-seed}";
|
||||||
|
|
||||||
|
mail-user = "selby-forum";
|
||||||
|
mail-password = mkPasswd "mail-password";
|
||||||
|
|
||||||
|
database-name = "forum_selby_ca";
|
||||||
|
database-user = "forum_selby_ca";
|
||||||
|
|
||||||
|
runtime-path = "/run/selby/forum";
|
||||||
|
|
||||||
|
domain-name = "selby.ca";
|
||||||
|
zone-name = config.fudo.domains.${domain-name}.zone;
|
||||||
|
|
||||||
|
forum-hostname = "${relative-hostname}.${domain-name}";
|
||||||
|
|
||||||
|
in {
|
||||||
|
networking = mkIf cfg.enable {
|
||||||
|
nat = {
|
||||||
|
enable = true;
|
||||||
|
internalInterfaces = [ "ve-selby-forum" ];
|
||||||
|
externalInterface = cfg.external-interface;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.nginx = mkIf cfg.enable {
|
||||||
|
enable = true;
|
||||||
|
recommendedOptimisation = true;
|
||||||
|
recommendedProxySettings = true;
|
||||||
|
|
||||||
|
virtualHosts.${forum-hostname} = {
|
||||||
|
enableACME = true;
|
||||||
|
forceSSL = true;
|
||||||
|
locations."/".proxyPass = "http://${child-ip}:80";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
fudo = {
|
||||||
|
# Email user
|
||||||
|
system-users.${mail-user} = {
|
||||||
|
description = "Selby Forum";
|
||||||
|
ldap-hashed-password =
|
||||||
|
pkgs.lib.passwd.hash-ldap-passwd "selby-forum-mail-passwd"
|
||||||
|
mail-password;
|
||||||
|
};
|
||||||
|
|
||||||
|
zones.${zone-name}.aliases = { ${relative-hostname} = "${host-fqdn}."; };
|
||||||
|
|
||||||
|
secrets.host-secrets.${hostname} = mkIf cfg.enable
|
||||||
|
(let db-passwd = mkPasswd "database-password";
|
||||||
|
in {
|
||||||
|
selby-forum-database-password = {
|
||||||
|
source-file = db-passwd;
|
||||||
|
target-file = "${runtime-path}/db.passwd";
|
||||||
|
};
|
||||||
|
postgres-selby-forum-password = {
|
||||||
|
source-file = db-passwd;
|
||||||
|
target-file = "/run/postgres-users/selby-forum.passwd";
|
||||||
|
user = config.services.postgresql.superUser;
|
||||||
|
};
|
||||||
|
selby-forum-admin-passwd = {
|
||||||
|
source-file = mkPasswd "admin-password";
|
||||||
|
target-file = "${runtime-path}/admin.passwd";
|
||||||
|
};
|
||||||
|
selby-forum-mail-passwd = {
|
||||||
|
source-file = mail-password;
|
||||||
|
target-file = "${runtime-path}/mail.passwd";
|
||||||
|
};
|
||||||
|
legacy-selby-forum-data = {
|
||||||
|
source-file = "${clean-utf8-file cfg.legacy-forum-data}";
|
||||||
|
target-file = "${runtime-path}/selby-forum-data.sql";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
postgresql = mkIf cfg.enable {
|
||||||
|
local-networks = [ "192.168.92.0/30" ];
|
||||||
|
databases.${database-name}.users = config.instance.local-admins;
|
||||||
|
users.${database-user} = {
|
||||||
|
password-file =
|
||||||
|
host-secrets.postgres-selby-forum-password.target-file;
|
||||||
|
databases.${database-name} = {
|
||||||
|
access = "CONNECT,CREATE";
|
||||||
|
entity-access = {
|
||||||
|
"ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE";
|
||||||
|
"ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd = mkIf cfg.enable {
|
||||||
|
tmpfiles.rules = [ "d ${cfg.state-directory} 750 - - - -" ];
|
||||||
|
|
||||||
|
services.discourse-prepare-selby-forum = {
|
||||||
|
description =
|
||||||
|
"Perform superuser tasks for Discourse, which doesn't have SU perms.";
|
||||||
|
wantedBy = [ "container@selby-forum.service" ];
|
||||||
|
before = [ "container@selby-forum.service" ];
|
||||||
|
requires = [ config.fudo.postgresql.systemd-target ];
|
||||||
|
after = [ config.fudo.postgresql.systemd-target ];
|
||||||
|
path = with pkgs; [ postgresql ];
|
||||||
|
serviceConfig = {
|
||||||
|
User = config.services.postgresql.superUser;
|
||||||
|
ExecStart =
|
||||||
|
pkgs.writeShellScript "discourse-prepare-selby-forum.sh" ''
|
||||||
|
psql -d ${database-name} -c "CREATE EXTENSION IF NOT EXISTS hstore;"
|
||||||
|
psql -d ${database-name} -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
containers.selby-forum = mkIf cfg.enable {
|
||||||
|
ephemeral = true;
|
||||||
|
privateNetwork = true;
|
||||||
|
hostAddress = parent-ip;
|
||||||
|
localAddress = child-ip;
|
||||||
|
autoStart = true;
|
||||||
|
|
||||||
|
bindMounts = {
|
||||||
|
${runtime-path} = {
|
||||||
|
hostPath = runtime-path;
|
||||||
|
isReadOnly = true;
|
||||||
|
};
|
||||||
|
"/var/lib/discourse" = {
|
||||||
|
hostPath = cfg.state-directory;
|
||||||
|
isReadOnly = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = { config, lib, ... }:
|
||||||
|
let
|
||||||
|
discourse-user = config.systemd.services.discourse.serviceConfig.User;
|
||||||
|
in {
|
||||||
|
networking = {
|
||||||
|
defaultGateway = parent-ip;
|
||||||
|
firewall.enable = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.discourse = {
|
||||||
|
enable = true;
|
||||||
|
hostname = forum-hostname;
|
||||||
|
enableACME = false;
|
||||||
|
admin = {
|
||||||
|
username = "admin";
|
||||||
|
fullName = "Admin";
|
||||||
|
email = "admin@selby.ca";
|
||||||
|
passwordFile = "/etc/selby-forum/admin.passwd";
|
||||||
|
};
|
||||||
|
database = {
|
||||||
|
name = database-name;
|
||||||
|
host = parent-ip;
|
||||||
|
username = database-user;
|
||||||
|
passwordFile = "/etc/selby-forum/db.passwd";
|
||||||
|
};
|
||||||
|
mail.outgoing = {
|
||||||
|
username = mail-user;
|
||||||
|
passwordFile = "/etc/selby-forum/mail.passwd";
|
||||||
|
domain = domain-name;
|
||||||
|
forceTLS = true;
|
||||||
|
serverAddress = cfg.mail.host;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.etc = {
|
||||||
|
"selby-forum/admin.passwd" = {
|
||||||
|
source = "/run/selby/forum/admin.passwd";
|
||||||
|
user = discourse-user;
|
||||||
|
mode = "0400";
|
||||||
|
};
|
||||||
|
"selby-forum/db.passwd" = {
|
||||||
|
source = "/run/selby/forum/db.passwd";
|
||||||
|
user = discourse-user;
|
||||||
|
mode = "0400";
|
||||||
|
};
|
||||||
|
"selby-forum/selby-forum-data.sql" = {
|
||||||
|
source = "/run/selby/forum/selby-forum-data.sql";
|
||||||
|
user = discourse-user;
|
||||||
|
mode = "0400";
|
||||||
|
};
|
||||||
|
"selby-forum/mail.passwd" = {
|
||||||
|
source = "/run/selby/forum/mail.passwd";
|
||||||
|
user = discourse-user;
|
||||||
|
mode = "0400";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd = {
|
||||||
|
tmpfiles.rules =
|
||||||
|
[ "d /var/lib/discourse 700 ${discourse-user} - - -" ];
|
||||||
|
|
||||||
|
services = {
|
||||||
|
discourse = { after = [ "multi-user.target" ]; };
|
||||||
|
|
||||||
|
discourse-import-selby-forum = let
|
||||||
|
env-without-path = filterAttrs (attr: _: attr != "PATH")
|
||||||
|
config.systemd.services.discourse.environment;
|
||||||
|
in {
|
||||||
|
description = "One-off job to import Vanilla Selby Forum data.";
|
||||||
|
path = config.systemd.services.discourse.path;
|
||||||
|
environment = env-without-path;
|
||||||
|
serviceConfig = {
|
||||||
|
User = discourse-user;
|
||||||
|
type = "oneshot";
|
||||||
|
WorkingDirectory =
|
||||||
|
config.systemd.services.discourse.serviceConfig.WorkingDirectory;
|
||||||
|
ExecStart = pkgs.writeShellScript
|
||||||
|
"import-vanilla-selby-forum-data.sh" ''
|
||||||
|
ruby script/import_scripts/vanilla.rb /etc/selby-forum/selby-forum-data.sql
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
hostname = config.instance.hostname;
|
||||||
|
host = config.fudo.hosts.${hostname};
|
||||||
|
domain-name = config.instance.local-domain;
|
||||||
|
domain = config.fudo.domains.${domain-name};
|
||||||
|
|
||||||
|
gateway = domain.wireguard.gateway;
|
||||||
|
|
||||||
|
is-wireguard-client =
|
||||||
|
gateway != null && gateway != hostname &&
|
||||||
|
host.wireguard.private-key-file != null;
|
||||||
|
|
||||||
|
in {
|
||||||
|
config = mkIf is-wireguard-client (let
|
||||||
|
|
||||||
|
in {
|
||||||
|
fudo.wireguard-client = {
|
||||||
|
enable = true;
|
||||||
|
server = {
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
hostname = config.instance.hostname;
|
||||||
|
host = config.fudo.hosts.${hostname};
|
||||||
|
host-secrets = config.fudo.secrets.host-secrets.${hostname};
|
||||||
|
|
||||||
|
cfg = config.fudo.services.wireguard-gateway;
|
||||||
|
|
||||||
|
peerOpts = { name, ... }: {
|
||||||
|
options = with types; {
|
||||||
|
public-key = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "Peer public key.";
|
||||||
|
};
|
||||||
|
|
||||||
|
assigned-ip = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "IP address assigned to this peer.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
in {
|
||||||
|
options.fudo.services.wireguard-gateway = with types; {
|
||||||
|
enable = mkEnableOption "Enable WireGuard gateway: let external clients join the local network.";
|
||||||
|
|
||||||
|
network = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "IP address range to use for clients.";
|
||||||
|
default = "172.16.0.0/24";
|
||||||
|
};
|
||||||
|
|
||||||
|
listen-port = mkOption {
|
||||||
|
type = port;
|
||||||
|
description = "Port on which to listen for incoming connections.";
|
||||||
|
default = 51820;
|
||||||
|
};
|
||||||
|
|
||||||
|
peers = mkOption {
|
||||||
|
type = attrsOf (submodule peerOpts);
|
||||||
|
description = "Map of peer to peer options.";
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = all (peerOpts:
|
||||||
|
pkgs.lib.ip.ipv4OnNetwork peerOpts.assigned-ip cfg.network)
|
||||||
|
(attrValues cfg.peers);
|
||||||
|
message = "Peer IPs must be on the assigned network.";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
assertion = host.wireguard.private-key-file != null;
|
||||||
|
message = "WireGuard gateway server private key file must be set.";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
fudo.secrets.host-secrets.${hostname} = {
|
||||||
|
wireguard-gateway-privkey-file = {
|
||||||
|
source-file = host.wireguard.private-key-file;
|
||||||
|
target-file = "/run/wireguard-gateway/key";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking = {
|
||||||
|
firewall.allowedUDPPorts = [ cfg.listen-port ];
|
||||||
|
wireguard.interfaces.wg-gw0 = {
|
||||||
|
ips = [ cfg.network ];
|
||||||
|
listenPort = cfg.listen-port;
|
||||||
|
postSetup =
|
||||||
|
"${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s ${cfg.network} -o ${cfg.external-interface} -j MASQUERADE";
|
||||||
|
postShutdown =
|
||||||
|
"${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s ${cfg.network} -o ${cfg.external-interface} -j MASQUERADE";
|
||||||
|
|
||||||
|
privateKeyFile = host-secrets.wireguard-gateway-privkey-file.target-file;
|
||||||
|
|
||||||
|
peers = let
|
||||||
|
peerList = attrValues cfg.peers;
|
||||||
|
in map (peerOpts: {
|
||||||
|
publicKey = peerOpts.public-key;
|
||||||
|
allowedIPs = [ "${peerOpts.ip}/32" ];
|
||||||
|
}) cfg.peers;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
hostOpts = { name, ... }: {
|
||||||
|
options = with types; {
|
||||||
|
ip = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "IP of this host on the WireGuard network.";
|
||||||
|
};
|
||||||
|
|
||||||
|
public-key = mkOption {
|
||||||
|
type = str;
|
||||||
|
description = "WireGuard public key of this host.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
wg-keys = config.fudo.secrets.files.wireguard.keys;
|
||||||
|
|
||||||
|
in {
|
||||||
|
options.fudo.services.wireguard = with types; {
|
||||||
|
hosts = mkOption {
|
||||||
|
type = attrsOf (submodule hostOpts);
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./service/backplane.nix
|
||||||
|
./service/chute.nix
|
||||||
|
./service/dns.nix
|
||||||
|
./service/fudo-auth.nix
|
||||||
|
./service/jabber.nix
|
||||||
|
./service/local-network.nix
|
||||||
|
./service/logging.nix
|
||||||
|
./service/mail-server.nix
|
||||||
|
./service/metrics.nix
|
||||||
|
./service/postgresql.nix
|
||||||
|
./service/selby-forum.nix
|
||||||
|
# ./service/wireguard-gateway.nix
|
||||||
|
];
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
wg-keys = config.fudo.secrets.files.wireguard.keys;
|
||||||
|
|
||||||
|
has-key = hostname: _: hasAttr hostname wg-keys;
|
||||||
|
|
||||||
|
keyed-hosts = filterAttrs has-key config.fudo.hosts;
|
||||||
|
|
||||||
|
sites = config.fudo.sites;
|
||||||
|
|
||||||
|
generatePublicKeyPkg = hostname: privkey-file: pkgs.stdenv.mkDerivation {
|
||||||
|
name = "wireguard-${hostname}-key.pub";
|
||||||
|
phases = "installPhase";
|
||||||
|
buildInputs = [ pkgs.wireguard ];
|
||||||
|
installPhase = ''
|
||||||
|
wg pubkey < ${privkey-file} > $out
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
generatePublicKey = hostname: privkey-file:
|
||||||
|
readFile "${generatePublicKeyPkg hostname privkey-file}";
|
||||||
|
|
||||||
|
in {
|
||||||
|
config = {
|
||||||
|
fudo.services.wireguard.networks = {
|
||||||
|
fudo-local = {
|
||||||
|
network = "10.0.0.0/8";
|
||||||
|
captured-network = "10.192.0.0/10";
|
||||||
|
|
||||||
|
external-peers = {
|
||||||
|
niten-phone = {
|
||||||
|
public-key = "";
|
||||||
|
assigned-ip = "10.192.0.100";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
hosts = mapAttrs (hostname: hostOpts: let
|
||||||
|
private-key-file = wg-keys.${hostname};
|
||||||
|
in {
|
||||||
|
inherit private-key-file;
|
||||||
|
public-key = generatePublicKey hostname private-key-file;
|
||||||
|
}) keyed-hosts;
|
||||||
|
|
||||||
|
sites = {
|
||||||
|
seattle = {
|
||||||
|
network = sites.seattle.private-network;
|
||||||
|
gateway = sites.seattle.local-gateway;
|
||||||
|
};
|
||||||
|
|
||||||
|
nuttyclub = {
|
||||||
|
network = sites.nuttyclub.private-network;
|
||||||
|
gateway = "nutboy3";
|
||||||
|
};
|
||||||
|
|
||||||
|
portage = {
|
||||||
|
network = sites.portage.private-network;
|
||||||
|
gateway = "france";
|
||||||
|
};
|
||||||
|
|
||||||
|
worldstream = {
|
||||||
|
network = sites.worldstream.private-network;
|
||||||
|
gateway = "legatus";
|
||||||
|
};
|
||||||
|
|
||||||
|
russell = {
|
||||||
|
network = sites.russell.private-network;
|
||||||
|
gateway = sites.russell.local-gateway;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./zones/selby.ca.nix
|
||||||
|
];
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
let
|
||||||
|
fudo = config.fudo.zones."fudo.org";
|
||||||
|
|
||||||
|
getIfAttrs = attrs: attrmap: let
|
||||||
|
has-attr = attr: hasAttr attr attrmap;
|
||||||
|
in getAttrs (filter has-attr attrs) attrmap;
|
||||||
|
|
||||||
|
in {
|
||||||
|
config = {
|
||||||
|
fudo.zones."selby.ca" = {
|
||||||
|
srv-records = let
|
||||||
|
# Mail records will be created, no need to copy
|
||||||
|
shared-tcp-attrs =
|
||||||
|
["domain"
|
||||||
|
"kerberos"
|
||||||
|
"kerberos-adm"
|
||||||
|
"ldap"
|
||||||
|
"ldaps"
|
||||||
|
"minecraft"
|
||||||
|
"xmpp-client"
|
||||||
|
"xmpp-server"];
|
||||||
|
|
||||||
|
shared-udp-attrs =
|
||||||
|
[
|
||||||
|
"kerberos"
|
||||||
|
"kerberos-master"
|
||||||
|
"kpasswd"
|
||||||
|
];
|
||||||
|
in {
|
||||||
|
tcp = getIfAttrs shared-tcp-attrs fudo.srv-records.tcp;
|
||||||
|
udp = getIfAttrs shared-udp-attrs fudo.srv-records.udp;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
{
|
||||||
|
description = "Live Disk Flake";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "nixpkgs/nixos-21.05";
|
||||||
|
|
||||||
|
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.
|
||||||
|
fudo-entities = {
|
||||||
|
url = "git+https://git.fudo.org/fudo-nix/entities.git";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
|
fudo-lib = {
|
||||||
|
url = "git+https://git.fudo.org/fudo-nix/lib.git";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
|
fudo-pkgs.url = "git+https://git.fudo.org/fudo-nix/pkgs.git";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, fudo-home, fudo-entities, fudo-lib, fudo-pkgs, ...
|
||||||
|
}@inputs: {
|
||||||
|
nixosConfigurations.live-cd-x86_64-linux = let
|
||||||
|
system = "x86_64-linux";
|
||||||
|
pkgs = import nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
config.allowUnfree = true;
|
||||||
|
overlays = [ fudo-pkgs.overlay ];
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
inherit system;
|
||||||
|
modules = [
|
||||||
|
({ config, ... }: {
|
||||||
|
imports = [
|
||||||
|
fudo-home.nixosModule
|
||||||
|
"${nixpkgs}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix"
|
||||||
|
"${nixpkgs}/nixos/modules/installer/cd-dvd/channel.nix"
|
||||||
|
];
|
||||||
|
config = with pkgs.lib; {
|
||||||
|
environment.etc.nixos-live.source = ./.;
|
||||||
|
hardware.enableAllFirmware = true;
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
btrfs-progs
|
||||||
|
emacs
|
||||||
|
git
|
||||||
|
parted
|
||||||
|
gparted
|
||||||
|
nix-prefetch-scripts
|
||||||
|
wget
|
||||||
|
];
|
||||||
|
|
||||||
|
services.openssh = {
|
||||||
|
enable = true;
|
||||||
|
startWhenNeeded = true;
|
||||||
|
permitRootLogin = mkDefault "prohibit-password";
|
||||||
|
};
|
||||||
|
|
||||||
|
i18n.defaultLocale = "en_US.UTF-8";
|
||||||
|
console.useXkbConfig = true;
|
||||||
|
|
||||||
|
services.xserver = {
|
||||||
|
layout = "us";
|
||||||
|
xkbVariant = "dvp";
|
||||||
|
xkbOptions = "ctrl:nocaps";
|
||||||
|
};
|
||||||
|
|
||||||
|
nix = {
|
||||||
|
package = pkgs.nixFlakes;
|
||||||
|
extraOptions = "experimental-features = nix-command flakes";
|
||||||
|
};
|
||||||
|
|
||||||
|
programs = {
|
||||||
|
ssh = {
|
||||||
|
startAgent = true;
|
||||||
|
|
||||||
|
package = pkgs.openssh_gssapi;
|
||||||
|
|
||||||
|
extraConfig = ''
|
||||||
|
GSSAPIAuthentication yes
|
||||||
|
GSSAPIDelegateCredentials yes
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
krb5.libdefaults.default_realm = "FUDO.ORG";
|
||||||
|
|
||||||
|
users.users = {
|
||||||
|
niten = {
|
||||||
|
isNormalUser = true;
|
||||||
|
createHome = true;
|
||||||
|
hashedPassword =
|
||||||
|
"$6$a1q2Duoe35hd5$IaZGXPfqyGv9uq5DQm7DZq0vIHsUs39sLktBiBBqMiwl/f/Z4jSvNZLJp9DZJYe5u2qGBYh1ca.jsXvQA8FPZ/";
|
||||||
|
extraGroups = [ "wheel" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
root.openssh.authorizedKeys.keys = [
|
||||||
|
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDGVez4of30f+j0cWKj5kYCKeFjyNsYvG9UbOMxF5hImD2lP5MSbFBv31gFgHjx3yCG4zQRZlpuyU5uWo0qIwe9N84/LcZcB9WrWKZXDmuof7zPFy0J+Hj+LVLDQI/mVXHNwkMhBMHpPrdwA05EYDAYCYklWT4cSByu10pHtST+olF8i+A+UQgUzgNZzdJVeiYZv6MBDTYsJWptGeDUkl2B0Es3gtbGYcCCfnyS3RC7DIXlDo3NBbAr7WaHY2MBbT+R/+jicn9E3IY3NCM5jENxqmvHy9MDsxEEYgFNm7IDwq4V1VRUWy277YsvRbmEaHb+osOA5u1VNN4z3UftOZcSZgR5C/vR71cENXoPt1YQpCzu7i38ojtvL+tDVEKT7sIovrQw8q1sszNlW2nXh8RSPiIq5TMnrV73MP0egKcr9n3tfxwi1BIkLjvfom/02BkTK9R9v+VMNhYU1YwROhORCiMIgoxUGiUvtH8u38JGr7E0hhMoAjCE5k80WPUivl0="
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue