Huge commit...like usual I guess

This commit is contained in:
2023-10-14 16:15:26 -07:00
parent b93f7d5b66
commit e3955ba861
54 changed files with 5650 additions and 2225 deletions
+98 -1
View File
@@ -1,4 +1,101 @@
{ config, lib, pkgs, ... }:
{
with lib;
let
hostname = config.instance.hostname;
localDomain = "fudo.org";
serviceSecrets = config.fudo.secrets.files.service-secrets."${hostname}";
domain = config.fudo.domains."${localDomain}";
authentikHost = "legatus";
primaryNameserver = "germany";
in {
imports = [
(import ./fudo.org/authentik.nix { inherit authentikHost; })
(import ./fudo.org/mastodon.nix {
mastodonHost = "legatus";
mastodonHostname = "mastodon.fudo.org";
mastodonOidcClientId = serviceSecrets."mastodon-oidc.clientid";
mastodonOidcClientSecret = serviceSecrets."mastodon-oidc.secret";
})
(import ./fudo.org/nextcloud.nix {
nextcloudHost = "legatus";
nextcloudHostname = "cloud.fudo.org";
nextcloudPackage = pkgs.nextcloud27;
})
(import ./fudo.org/matrix.nix {
matrixHost = "legatus";
matrixServerName = "fudo.org";
openIdClientId = readFile serviceSecrets."matrix-oidc.clientid";
openIdClientSecret = readFile serviceSecrets."matrix-oidc.secret";
})
(import ./fudo.org/mail-server.nix (rec {
primaryMailserver = "germany";
primaryDomain = "test.fudo.org";
authentikServer = "authentik.fudo.org";
ldapBase = "dc=fudo,dc=org";
ldapBindDn = "cn=userdb,ou=users,${ldapBase}";
ldapBindPwFile =
config.fudo.secrets.files.domain-secrets."${primaryDomain}"."ldap-bind.passwd";
saslDomain = "FUDO.ORG";
authentikOutpostToken =
config.fudo.secrets.files.domain-secrets."${primaryDomain}"."authentik-ldap.token";
servedDomains =
[ "fudo.org" "fudo.ca" "fudo.im" "selby.ca" "selbyhomecentre.com" ];
# TODO: FIXME!
dkimRecord = "";
}))
];
config = {
# All Fudo hosts should redirect selby.ca to the selbyhomecentre website.
services.nginx.virtualHosts = {
"selby.ca" = {
enableACME = true;
locations."/".return = "301 https://selbyhomecentre.com$request_uri";
};
"www.selby.ca" = {
enableACME = true;
locations."/".return = "301 https://selbyhomecentre.com$request_uri";
};
"selbyhomecentre.com" = {
enableACME = true;
locations."/".return = "301 https://selbyhomecentre.com$request_uri";
};
"www.selbyhomecentre.com" = {
enableACME = true;
locations."/".return = "301 https://selbyhomecentre.com$request_uri";
};
};
fudo.services = {
jabber = {
domain = "jabber.fudo.org";
ldap.servers = map (host: "${host}.${localDomain}") domain.ldap-servers;
};
authoritative-dns = {
enable = hostname == primaryNameserver;
nameservers = { primary = primaryNameserver; };
zones = {
"fudo.org" = {
default-host = "germany";
ksk = config.fudo.secrets.files.dns.key-signing-keys."fudo.org";
};
"selby.ca" = {
default-host = "germany";
ksk = config.fudo.secrets.files.dns.key-signing-keys."selby.ca";
};
"selbyhomecentre.com" = {
default-host = "germany";
ksk = null;
};
};
};
};
};
}
@@ -0,0 +1,75 @@
{ authentikHost, ... }:
{ config, lib, pkgs, ... }:
with lib;
let
hostname = config.instance.hostname;
host = config.fudo.hosts."${hostname}";
domainName = host.domain;
zoneName = config.fudo.domains."${domainName}".zone;
isAuthentik = hostname == authentikHost;
authentikHostname = "authentik.${domainName}";
in {
config = {
fudo.zones."${zoneName}".aliases.authentik = authentikHost;
services = {
authentikContainer = mkIf isAuthentik {
enable = true;
images = {
authentik = "ghcr.io/goauthentik/server:2023.8.3";
postgres = "docker.io/library/postgres:12-alpine";
redis = "docker.io/library/redis:alpine";
};
smtp = {
host = "mail.fudo.org";
password-file =
config.fudo.secrets.files.service-passwords."${authentikHost}".authentik-smtp;
};
};
nginx = mkIf isAuthentik {
enable = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
virtualHosts = {
"${authentikHostname}" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${
toString config.services.authentikContainer.ports.http
}";
proxyWebsockets = true;
};
};
"fudo.ldap.fudo.org" = {
enableACME = true;
forceSSL = true;
locations."/".return = "403 Forbidden";
};
"selby.ldap.fudo.org" = {
enableACME = true;
forceSSL = true;
locations."/".return = "403 Forbidden";
};
};
};
};
security.acme.certs = mkIf isAuthentik
(genAttrs [ authentikHostname "fudo.ldap.fudo.org" "selby.ldap.fudo.org" ]
(domain: {
postRun = let
dst =
"${config.services.authentikContainer.state-directory}/certs/${domain}";
in ''
mkdir -p ${dst}
cp -v {cert,chain,fullchain,full,key}.pem ${dst}/
cp -v key.pem ${dst}/privkey.pem
chown -R authentik ${dst}
'';
}));
};
}
@@ -0,0 +1,103 @@
{ primaryMailserver, primaryDomain, authentikServer, servedDomains, ldapBase
, ldapBindDn, ldapBindPwFile, dkimRecord, saslDomain, authentikOutpostToken, ...
}:
{ config, lib, pkgs, ... }:
with lib;
let hostname = config.instance.hostname;
in {
config = {
fudo = {
acme.host-domains = {
"imap.${primaryDomain}".extra-domain = [ "mail.${primaryDomain}" ];
"smtp.${primaryDomain}".extra-domain = [ "mail.${primaryDomain}" ];
};
zones."${primaryDomain}" = let
mailserverDomain = config.fudo.hosts."${primaryMailserver}".domain;
mailserverZone = config.fudo.domains."${mailserverDomain}".zone;
mailserverIps =
config.fudo.zones."${mailserverZone}".hosts."${mailserver}";
in {
hosts = {
imap = {
ipv4-address = mailserverIps.ipv4-address;
ipv6-address = mailserverIps.ipv6-address;
};
smtp = {
ipv4-address = mailserverIps.ipv4-address;
ipv6-address = mailserverIps.ipv6-address;
};
mail = {
ipv4-address = mailserverIps.ipv4-address;
ipv6-address = mailserverIps.ipv6-address;
};
};
# FIXME: DKIM key!!!
verbatim-dns-records = [ dkimRecord ];
};
mail = {
enable = hostname == primaryMailserver;
debug = true;
primary-domain = primaryDomain;
sasl-domain = saslDomain;
trusted-networks = config.instance.local-networks;
smtp = {
hostname = "smtp.${primaryDomain}";
ssl-directory =
config.security.acme.certs."smtp.${primaryDomain}".directory;
};
imap = {
hostname = "imap.${primaryDomain}";
ssl-directory =
config.security.acme.certs."imap.${primaryDomain}".directory;
};
ldap = {
authentik-host = "https://${authentikServer}";
outpost-token = readFile authentikOutpostToken;
base = ldapBase;
bind-dn = ldapBindDn;
bind-password-file = ldapBindPwFile;
};
aliases = let admins = config.instance.local-admins;
in {
alias-users = {
admin = admins;
dmarc-reports = admins;
ftp = admins;
hostmaster = admins;
irc = admins;
postmaster = admins;
root = admins;
system = admins;
webmaster = admins;
www-data = admins;
};
user-aliases =
let hasAliases = _: userOpts: userOpts.email-aliases != [ ];
in mapAttrs (_: userOpts: userOpts.email-aliases)
(filterAttrs hasAliases config.fudo.users);
};
};
};
services.nginx = mkIf (hostname == primaryMailserver) {
enable = true;
virtualHosts = {
"smtp.${primaryDomain}" = {
enableACME = true;
forceSSL = true;
locations."/".return = "301 https://webmail.${primaryDomain}";
};
"imap.${primaryDomain}" = {
enableACME = true;
forceSSL = true;
locations."/".return = "301 https://webmail.${primaryDomain}";
};
};
};
};
}
@@ -0,0 +1,57 @@
{ mastodonHost, mastodonHostname, mastodonOidcClientId, mastodonOidcClientSecret
, ... }:
{ config, lib, pkgs, ... }:
with lib;
let
hostname = config.instance.hostname;
isMastodon = hostname == mastodonHost;
in {
config = {
services = {
mastodonContainer = mkIf isMastodon {
enable = true;
hostname = mastodonHostname;
web-domain = "fudo.org";
version = "v4.1.6";
state-directory = "/state/services/mastodon";
smtp.server = "mail.fudo.org";
environment = {
OIDC_ENABLED = "true";
OIDC_DISPLAY_NAME = "fudo auth";
OIDC_DISCOVERY = "true";
OIDC_ISSUER = "https://authentik.fudo.org/application/o/mastodon/";
OIDC_AUTH_ENDPOINT =
"https://authentik.fudo.org/application/o/authorize/";
OIDC_SCOPE = "openid,profile,email";
OIDC_UID_FIELD = "sub";
OIDC_CLIENT_ID = readFile mastodonOidcClientId;
OIDC_CLIENT_SECRET = readFile mastodonOidcClientSecret;
OIDC_REDIRECT_URI =
"https://mastodon.fudo.org/auth/auth/openid_connect/callback";
OIDC_SECURITY_ASSUME_EMAIL_IS_VERIFIED = "true";
};
};
nginx = mkIf isMastodon {
enable = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
virtualHosts = {
"${mastodonHostname}" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${
toString config.services.mastodonContainer.port
}";
proxyWebsockets = true;
};
};
};
};
};
};
}
+93
View File
@@ -0,0 +1,93 @@
{ matrixHost, matrixServerName, openIdClientId, openIdClientSecret, ... }:
{ config, lib, pkgs, ... }:
with lib;
let
hostname = config.instance.hostname;
domainName = "fudo.org";
zoneName = config.fudo.domains."${domainName}".zone;
isMatrix = hostname == matrixHost;
matrixFqdn = "matrix.${domainName}";
in {
config = {
fudo = {
zones."${zoneName}".aliases = {
element = matrixHost;
matrix = matrixHost;
};
services.matrix = mkIf isMatrix {
enable = true;
server-name = matrixServerName;
hostname = matrixFqdn;
openid = {
client-id = openIdClientId;
client-secret = openIdClientSecret;
provider = "fudo-auth";
provider-name = "Fudo Auth";
issuer = "https://authentik.fudo.org/application/o/matrix/";
};
};
};
networking.firewall.allowedTCPPorts = [ 8008 8448 ];
services.nginx.virtualHosts = mkIf isMatrix {
"${domainName}" = let
mkWellKnown = data: ''
add_header Content-Type application/json;
add_header Access-Control-Allow-Origin *;
return 200 '${builtins.toJSON data}';
'';
in {
enableACME = true;
listen = [
{
addr = "0.0.0.0";
port = 8008;
ssl = false;
}
{
addr = "0.0.0.0";
port = 8448;
ssl = true;
}
{
addr = "0.0.0.0";
port = 80;
ssl = false;
}
{
addr = "0.0.0.0";
port = 443;
ssl = true;
}
];
locations."/.well-known/matrix/server".extraConfig =
mkWellKnown { "m.server" = "${matrixFqdn}:443"; };
locations."/.well-known/matrix/client".extraConfig =
mkWellKnown { "m.homeserver".base_url = "https://${matrixFqdn}"; };
};
# "${matrixFqdn}" = {
# locations."^/$".return = "301 https://element.${domainName}";
# };
"element.${domainName}" = {
enableACME = true;
forceSSL = true;
root = pkgs.element-web.override {
conf = {
default_server_name = domainName;
default_server_config."m.homeserver".base_url =
"https://${matrixFqdn}";
brand = "Fudo";
room_directory.servers =
[ "fudo.org" "matrix.org" "libera.chat" "gitter.im" ];
map_style_url =
"https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx";
};
};
};
};
};
}
@@ -0,0 +1,45 @@
{ nextcloudPackage, nextcloudHost, nextcloudHostname, ... }:
{ config, lib, pkgs, ... }:
with lib;
let
hostname = config.instance.hostname;
host = config.fudo.hosts."${hostname}";
isNextcloud = hostname == nextcloudHost;
port = config.services.nextcloudContainer.port;
in {
config = mkIf isNextcloud {
services = {
nextcloudContainer = {
enable = true;
hostname = nextcloudHostname;
package = nextcloudPackage;
extra-apps =
with config.services.nextcloudContainer.package.packages.apps; {
inherit news contacts calendar tasks maps memories mail bookmarks
files_markdown notes unsplash user_saml;
};
timezone = "America/Winnipeg";
};
nginx = {
enable = true;
recommendedOptimisation = true;
recommendedGzipSettings = true;
recommendedProxySettings = true;
virtualHosts."${nextcloudHostname}" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${toString port}";
proxyWebsockets = true;
};
};
};
};
};
}
+133 -2
View File
@@ -1,4 +1,135 @@
{ config, lib, pkgs, ... }:
{
}
with lib;
let
hostname = config.instance.hostname;
mailserver = "locum";
authentikHost = "locum";
userdbPasswd = pkgs.lib.passwd.stablerandom-passwd-file "userdb-passwd"
config.instance.build-seed;
in {
imports =
[ (import ./informis.land/authentik.nix { inherit authentikHost; }) ];
config = {
fudo = {
acme.host-domains = {
"imap.informis.land".extra-domains = [ "mail.informis.land" ];
"smtp.informis.land".extra-domains = [ "mail.informis.land" ];
};
zones."informis.land" = let
mailserverIps = config.fudo.zones."informis.land".hosts."${mailserver}";
in {
hosts = {
imap = {
ipv4-address = mailserverIps.ipv4-address;
ipv6-address = mailserverIps.ipv6-address;
};
smtp = {
ipv4-address = mailserverIps.ipv4-address;
ipv6-address = mailserverIps.ipv6-address;
};
mail = {
ipv4-address = mailserverIps.ipv4-address;
ipv6-address = mailserverIps.ipv6-address;
};
};
verbatim-dns-records = [
''
mail._domainkey IN TXT ( "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDKiTSUDxDPwMxLMT7wzR0ZaGuGzU1xnhti0zqi6xGJVxe9O8wfpX1vTSAasGYGzg5r24Hc8tSTogUgy0uJXsIbPBiqXj3WsoL/vf7+tAmrmZA4DQn1hN+C0R/7knhTNPWKnIAMqReAH/yf3XvFGmBMpU3UGRNGc2MoCQ4iXBcbsQIDAQAB" ) ; ----- DKIM key mail for all-domains-generic-key''
];
# srv-records = let srv-record = host: port: [{ inherit host port; }];
# in {
# tcp = {
# imap = srv-record "imap.informis.land" 143;
# imaps = srv-record "imap.informis.land" 993;
# smtp = srv-record "smtp.informis.land" 25;
# submission = srv-record "smtp.informis.land" 587;
# };
# };
};
services.dns.zones."informis.land" = {
default-host = pkgs.lib.getHostIpv4 "locum";
};
postgresql.package = pkgs.postgresql_15_gssapi;
system-users.userdb = {
description = "User Database Lookup.";
ldap-hashed-password =
pkgs.lib.passwd.hash-ldap-passwd "userdb-passwd.hashed" userdbPasswd;
};
secrets.host-secrets."${hostname}".userdbPasswd = {
source-file = userdbPasswd;
target-file = "/run/ldap/userdbPasswd";
};
mail = {
enable = hostname == mailserver;
debug = false;
primary-domain = "informis.land";
sasl-domain = "INFORMIS.LAND";
trusted-networks = config.instance.local-networks;
smtp = {
hostname = "smtp.informis.land";
ssl-directory =
config.security.acme.certs."smtp.informis.land".directory;
};
imap = {
hostname = "imap.informis.land";
ssl-directory =
config.security.acme.certs."imap.informis.land".directory;
};
ldap = {
authentik-host = "https://authentik.informis.land";
outpost-token = readFile
config.fudo.secrets.files.service-secrets."${hostname}"."authentik-ldap.token";
base = "dc=informis,dc=land";
bind-dn = "cn=userdb,ou=users,dc=informis,dc=land";
bind-password-file =
config.fudo.secrets.files.service-passwords.locum.userdb;
};
aliases = {
alias-users = let admins = config.instance.local-admins;
in {
admin = admins;
dmarc-reports = admins;
ftp = admins;
hostmaster = admins;
irc = admins;
postmaster = admins;
root = admins;
system = admins;
webmaster = admins;
www-data = admins;
};
user-aliases =
let hasAliases = _: userOpts: userOpts.email-aliases != [ ];
in mapAttrs (user: userOpts: userOpts.email-aliases)
(filterAttrs hasAliases config.fudo.users);
};
};
};
services.nginx = mkIf (hostname == mailserver) {
enable = true;
virtualHosts = {
"smtp.informis.land" = {
enableACME = true;
forceSSL = true;
locations."/".return = "404";
};
"imap.informis.land" = {
enableACME = true;
forceSSL = true;
locations."/".return = "404";
};
};
};
};
}
@@ -0,0 +1,79 @@
{ authentikHost, ... }:
{ config, lib, pkgs, ... }:
with lib;
let
hostname = config.instance.hostname;
host = config.fudo.hosts."${hostname}";
domainName = host.domain;
zoneName = config.fudo.domains."${domainName}".zone;
isAuthentik = hostname == authentikHost;
authentikHostname = "authentik.${domainName}";
smtpPasswd = pkgs.lib.passwd.stablerandom-passwd-file "authentik-smtp-passwd"
config.instance.build-seed;
in {
config = {
fudo = {
system-users.authentik = {
description = "Aunthentik system user.";
ldap-hashed-password =
pkgs.lib.passwd.hash-ldap-passwd "authentik-smtp-passwd.hashed"
smtpPasswd;
};
zones."${zoneName}".aliases.authentik = authentikHost;
};
systemd.services.arion-authentik = {
requires = [ config.fudo.secrets.secret-target ];
after = [ config.fudo.secrets.secret-target ];
};
services = {
authentikContainer = mkIf isAuthentik {
enable = true;
images = {
authentik = "ghcr.io/goauthentik/server:2023.8.3";
postgres = "docker.io/library/postgres:12-alpine";
redis = "docker.io/library/redis:alpine";
};
smtp = {
host = "mail.fudo.org";
password-file = smtpPasswd;
};
};
nginx = mkIf isAuthentik {
enable = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
virtualHosts = {
"${authentikHostname}" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://localhost:${
toString config.services.authentikContainer.ports.http
}";
proxyWebsockets = true;
};
};
};
};
};
security.acme.certs = mkIf isAuthentik (genAttrs [ authentikHostname ]
(domain: {
postRun = let
dst =
"${config.services.authentikContainer.state-directory}/certs/${domain}";
in ''
mkdir -p ${dst}
cp -v {cert,chain,fullchain,full,key}.pem ${dst}/
cp -v key.pem ${dst}/privkey.pem
chown -R authentik ${dst}
'';
}));
};
}
+5
View File
@@ -0,0 +1,5 @@
{ config, lib, pkgs, ... }:
{
}
+13 -8
View File
@@ -1,18 +1,23 @@
{ config, lib, pkgs, ... }:
let fudo = config.fudo.domains."fudo.org";
in {
config.fudo.domains."sea.fudo.org" = {
local-networks = fudo.local-networks;
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;
primary-mailserver = fudo.primary-mailserver;
ldap-servers = fudo.ldap-servers;
ldap-servers = fudo.ldap-servers;
xmpp-servers = fudo.xmpp-servers;
xmpp-servers = fudo.xmpp-servers;
};
};
};
}
+111
View File
@@ -0,0 +1,111 @@
{ config, lib, pkgs, ... }:
let generateMac = pkgs.lib.network.generate-mac-address;
in {
boot = {
initrd = {
availableKernelModules = [
"uhci_hcd"
"ehci_pci"
"ata_piix"
"ahci"
"usb_storage"
"floppy"
"sd_mod"
"sr_mod"
];
kernelModules = [ "dm-snapshot" ];
};
kernelModules = [ "kvm-intel" ];
extraModulePackages = [ ];
kernelPackages = pkgs.linuxPackages_latest;
loader.grub = {
enable = true;
devices = [
"/dev/disk/by-id/ata-Samsung_SSD_870_QVO_2TB_S6R4NJ0W702893V"
"/dev/disk/by-id/ata-Crucial_CT525MX300SSD1_171516B3CB40"
];
};
};
fileSystems = {
"/boot" = {
device = "/dev/disk/by-label/germany-boot";
fsType = "ext4";
options = [ "noatime" "noexec" ];
};
"/" = {
device = "root-tmpfs";
fsType = "tmpfs";
options = [ "mode=755" "noexec" ];
};
"/nix" = {
device = "/dev/disk/by-label/germany-data";
fsType = "btrfs";
options = [ "subvol=@nix" "compress=zstd" "noatime" ];
};
"/state" = {
device = "/dev/disk/by-label/germany-data";
fsType = "btrfs";
options = [ "subvol=@state" "compress=zstd" "noatime" "noexec" ];
};
"/var/log" = {
device = "/dev/disk/by-label/germany-data";
fsType = "btrfs";
options = [ "subvol=@logs" "compress=zstd" "noatime" "noexec" ];
};
"/home" = {
device = "/dev/disk/by-label/germany-data";
fsType = "btrfs";
options = [ "subvol=@home" "compress=zstd" "noatime" "noexec" ];
};
"/var/lib/acme" = {
device = "/dev/disk/by-label/germany-data";
fsType = "btrfs";
options = [ "subvol=@acme" "compress=zstd" "noatime" "noexec" ];
};
};
swapDevices = [{ device = "/dev/disk/by-label/germany-swap"; }];
nix.settings.max-jobs = lib.mkDefault 24;
hardware.bluetooth.enable = false;
nixpkgs.hostPlatform = "x86_64-linux";
hardware = {
cpu.intel.updateMicrocode = true;
enableAllFirmware = true;
};
networking = {
useDHCP = false;
macvlans = {
extif0 = {
interface = "enp5s0f0";
mode = "bridge";
};
extif1 = {
interface = "enp5s0f1";
mode = "bridge";
};
};
interfaces = {
extif0.macAddress = generateMac config.instance.hostname "extif0";
extif1.macAddress = generateMac config.instance.hostname "extif1";
};
};
}
+7 -2
View File
@@ -10,7 +10,6 @@ with lib; {
extraModulePackages = [ ];
loader.grub = {
enable = true;
version = 2;
device = "/dev/sda";
};
@@ -23,7 +22,7 @@ with lib; {
"/" = {
device = "root-tmpfs";
fsType = "tmpfs";
options = [ "mode=755" "noexec" ];
options = [ "mode=755" "noexec" "size=20G" ];
};
"/boot" = {
@@ -53,6 +52,12 @@ with lib; {
[ "subvol=@state" "compress=zstd" "noatime" "nodiratime" "noexec" ];
};
"/var/lib/containers/storage" = {
device = "/dev/disk/by-label/legatus-data";
fsType = "btrfs";
options = [ "subvol=@container-data" "noatime" "compress=zstd" "noexec" ];
};
# "/var/lib/acme" = {
# device = "/dev/disk/by-label/system";
# fsType = "btrfs";
+73
View File
@@ -0,0 +1,73 @@
{ config, lib, pkgs, modulesPath, ... }:
let generateMac = pkgs.lib.network.generate-mac-address;
in {
imports = [ (modulesPath + "/installer/scan/not-detected.nix") ];
config = {
boot = {
initrd.availableKernelModules = [ "ahci" "xhci_pci" ];
kernelModules = [ "kvm-intel" ];
loader.grub = {
enable = true;
device =
"/dev/disk/by-id/ata-Samsung_SSD_870_EVO_250GB_S6PENX0T445931Y";
};
kernelPackages = pkgs.linuxPackages_latest;
};
system.stateVersion = "23.05";
fileSystems = {
"/" = {
device = "locum-root";
fsType = "tmpfs";
options = [ "mode=755" "size=16G" "noexec" ];
};
"/boot" = {
device = "/dev/disk/by-label/locum-boot";
fsType = "ext4";
options = [ "noatime" "noexec" ];
};
"/state" = {
device = "/dev/disk/by-label/locum-data";
fsType = "btrfs";
options = [ "noatime" "compress=zstd" "noexec" "subvol=@state" ];
};
"/nix" = {
device = "/dev/disk/by-label/locum-data";
fsType = "btrfs";
options = [ "noatime" "compress=zstd" "subvol=@nix" ];
};
"/home" = {
device = "/dev/disk/by-label/locum-data";
fsType = "btrfs";
options = [ "noatime" "compress=zstd" "subvol=@home" ];
};
"/var/log" = {
device = "/dev/disk/by-label/locum-data";
fsType = "btrfs";
options = [ "noatime" "compress=zstd" "noexec" "subvol=@logs" ];
};
};
swapDevices = [{ device = "/dev/disk/by-label/locum-swap"; }];
nix.settings.max-jobs = lib.mkDefault 16;
networking = {
useDHCP = false;
macvlans = {
extif0 = {
interface = "eno1";
mode = "bridge";
};
};
interfaces.extif0 = {
macAddress = generateMac config.instance.hostname "extif0";
};
};
};
}
-1
View File
@@ -11,7 +11,6 @@ with lib; {
extraModulePackages = [ ];
loader.grub = {
enable = true;
version = 2;
device = "/dev/sda";
};
+72
View File
@@ -0,0 +1,72 @@
{ config, lib, pkgs, ... }:
let
primary-ip = "208.81.3.116";
hostname = config.instance.hostname;
host = config.fudo.hosts."${hostname}";
domainName = host.domain;
site = config.fudo.sites."${host.site}";
hostFqdn = "${hostname}.${domainName}";
hostSecrets = config.fudo.secrets.host-secrets."${hostname}";
in {
config = {
networking = {
enableIPv6 = true;
nameservers = [ "1.1.1.1" ];
defaultGateway = {
interface = "extif0";
address = site.gateway-v4;
};
interfaces.extif0 = {
ipv4.addresses = [{
address = primary-ip;
prefixLength = 28;
}];
};
firewall.enable = {
enable = false;
interface.podman0.allowedUDPPorts = [ 53 ];
};
};
systemd.tmpfiles.rules = [
"L /etc/adjtime - - - - /state/etc/adjtime"
"d /state/services 0555 - - - -"
];
security.acme.defaults.email = "admin@fudo.org";
virtualisation = {
podman = {
enable = true;
autoPrune.enable = true;
dockerSocket.enable = true;
defaultNetwork.settings.dns_enabled = true;
};
oci-containers.backend = "podman";
arion.backend = "podman-socket";
};
fudo = {
client.dns = {
ipv4 = true;
ipv6 = true;
user = "fudo-client";
external-interface = "extif0";
};
mail.state-directory = "/state/services/mail";
nsd.zones."fudo.org".outgoingInterface = "extif0";
services = {
auth = {
kerberos.state-directory = "/state/services/kerberos";
ldap.state-directory = "/state/services/ldap";
};
authoritative-dns.state-directory = "/state/services/dns";
jabber.state-directory = "/state/services/jabber";
};
};
};
}
+53 -1
View File
@@ -29,9 +29,23 @@ in {
}];
};
# Getting errors due to running VMs
boot.kernel.sysctl."fs.inotify.max_user_instances" = 512;
security.acme.defaults.email = "admin@legatus.fudo.org";
systemd.tmpfiles.rules = [ "L /etc/adjtime - - - - /state/etc/adjtime" ];
systemd.tmpfiles.rules = [
"L /etc/adjtime - - - - /state/etc/adjtime"
"d /state/services/podman/volumes 0700 root root - -"
"d /state/services/acme 0755 acme acme - -"
];
fileSystems = {
"/var/lib/acme" = {
device = "/state/services/acme";
options = [ "bind" ];
};
};
environment.systemPackages = local-packages;
@@ -47,6 +61,37 @@ in {
# slynk-port = 4005;
# };
services = {
mastodonContainer.state-directory = "/state/services/mastodon";
lemmyDocker = {
enable = true;
hostname = "lemmy.fudo.org";
site-name = "Fudo Lemmy";
version = "0.18";
state-directory = "/state/services/lemmy";
smtp-server = "mail.fudo.org:587";
docker-images = {
pictrs = "asonix/pictrs:0.4";
postgres = "postgres:15-alpine";
};
};
authentikContainer.state-directory = "/state/services/authentik";
nextcloudContainer.state-directory = "/state/services/nextcloud";
nginx.virtualHosts."fudo.org" = {
enableACME = true;
forceSSL = true;
};
};
virtualisation = {
arion.backend = "podman-socket";
podman = {
enable = true;
dockerSocket.enable = true;
autoPrune.enable = true;
};
};
fudo = {
hosts.legatus.external-interfaces = [ "extif0" ];
@@ -66,6 +111,13 @@ in {
};
nexus.dns-server.listen-addresses = [ host-ipv4 ];
# lemmy = {
# enable = true;
# hostname = "lemmy.fudo.org";
# };
matrix.state-directory = "/state/services/matrix";
};
secrets.host-secrets.legatus = let files = config.fudo.secrets.files;
+23 -5
View File
@@ -50,27 +50,39 @@ in {
# internalInterfaces = [ "intif0" "intif1" "intif2" ];
# };
firewall = {
allowedTCPPorts = [ 80 443 25565 config.services.murmur.port ];
allowedUDPPorts = [ 25565 34197 ];
};
nat.forwardPorts = [
# Minecraft
{
destination = "10.0.0.10:25565";
destination = "10.0.0.12:25555";
proto = "tcp";
sourcePort = 25565;
}
{
destination = "10.0.0.10:25565";
destination = "10.0.0.12:25555";
proto = "udp";
sourcePort = 25565;
}
# Factorio
{
destination = "10.0.0.10:34197";
destination = "10.0.0.12:34197";
proto = "udp";
sourcePort = 34197;
}
];
};
fileSystems = {
"/var/lib/acme" = {
device = "/state/services/acme";
options = [ "bind" ];
};
};
fudo = {
hosts.limina.external-interfaces = [ "enp1s0" ];
@@ -120,12 +132,11 @@ in {
"L /root/.ssh/id_rsa - - - - /state/root/ssh/id_rsa"
"L /root/.ssh/id_rsa.pub - - - - /state/root/ssh/id_rsa.pub"
"L /root/.ssh/known_hosts - - - - /state/root/ssh/known_hosts"
"d /state/services/acme 0755 acme acme - -"
];
security.acme.defaults.email = "niten@fudo.org";
networking.firewall.allowedTCPPorts = [ 80 443 ];
systemd.services.nginx.requires = [ "bind.service" ];
services = {
@@ -151,6 +162,13 @@ in {
};
};
murmur = {
enable = true;
port = 64738;
bonjour = true;
password = "thelittleschool";
};
openssh = {
hostKeys = [
{
+112
View File
@@ -0,0 +1,112 @@
{ config, lib, pkgs, ... }:
with pkgs.lib;
let
hostname = "locum";
host-ipv4 = "190.2.134.48";
hostData = config.fudo.hosts."${hostname}";
site = config.fudo.sites."${hostData.site}";
hostSecrets = config.fudo.secrets.host-secrets."${hostname}";
in {
config = {
networking = {
enableIPv6 = true;
nameservers = [ "127.0.0.1" ];
defaultGateway = {
interface = "extif0";
address = site.gateway-v4;
};
interfaces.extif0 = {
ipv4.addresses = [{
address = host-ipv4;
prefixLength = 24;
}];
};
firewall = {
enable = false;
interfaces.podman0.allowedUDPPorts = [ 53 ];
};
};
virtualisation = {
podman = {
enable = true;
autoPrune.enable = true;
dockerSocket.enable = true;
defaultNetwork.settings.dns_enabled = true;
};
oci-containers.backend = "podman";
arion.backend = "podman-socket";
};
security.acme.defaults.email = "root@informis.land";
fudo = {
client.dns = {
ipv4 = true;
ipv6 = true;
user = "fudo-client";
external-interface = "extif0";
};
secure-dns-proxy = {
enable = true;
upstream-dns =
[ "https://1.1.1.1/dns-query" "https://1.0.0.1/dns-query" ];
bootstrap-dns = "1.1.1.1";
listen-ips = [ "127.0.0.1" ];
listen-port = 53;
allowed-networks =
[ "1.1.1.1/32" "1.0.0.1/32" "localhost" "link-local" ];
};
# mail-server = {
# clamav.state-directory = "/state/services/mail/clamav";
# dkim = { key-directory = "/state/services/mail/dkim"; };
# };
mail.state-directory = "/state/services/mail";
services = {
auth = {
kerberos.state-directory = "/state/services/kerberos";
ldap.state-directory = "/state/services/ldap";
};
logging.loki.state-directory = "/state/services/loki";
metrics = {
prometheus.state-directory = "/state/services/prometheus";
grafana.state-directory = "/state/services/grafana";
};
mail-server.state-directory = "/state/services/msailserver";
dns.zones."informis.land".enable = true;
};
};
services = {
authentikContainer.state-directory = "/state/services/authentik";
lemmyDocker = {
enable = true;
hostname = "informis.land";
site-name = "Informis Lemmy";
version = "0.18.2";
state-directory = "/state/services/lemmy";
smtp-server = "smtp.informis.land:587";
docker-images = {
pictrs = "asonix/pictrs:0.4.0-rc.14";
postgres = "postgres:15-alpine";
};
};
nginx = {
enable = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
};
};
};
}
+58 -31
View File
@@ -1,7 +1,15 @@
{ config, lib, pkgs, ... }:
with lib;
let hostname = "nostromo";
with pkgs.lib;
let
hostname = "nostromo";
domainName = config.fudo.hosts."${hostname}".domain;
domain = config.fudo.domains."${domainName}";
host-fqdn = pkgs.lib.getHostFqdn hostname;
in {
networking = {
@@ -27,12 +35,11 @@ in {
boot.kernelModules = [ "rpcsec_gss_krb5" ];
services = {
murmur.enable = true;
# objectifier = {
# enable = true;
# listen-addresses = [ "0.0.0.0" ];
# };
murmur = {
enable = true;
port = 64738;
bonjour = true;
};
nfs = {
# See ../user-config.nix for the user@REALM -> user mapping
@@ -50,6 +57,8 @@ in {
'';
};
};
authentikContainer.state-directory = "/state/services/authentik";
};
systemd = {
@@ -82,16 +91,16 @@ in {
host-secrets = config.fudo.secrets.host-secrets.${hostname};
in {
secrets.host-secrets.${hostname} = {
grafana-database-password = {
source-file = grafana-database-passwd-file;
target-file = "/run/services/grafana/db.passwd";
user = config.systemd.services.grafana.serviceConfig.User;
};
postgres-grafana-password = {
source-file = grafana-database-passwd-file;
target-file = "/run/services/postgres/db.passwd";
user = config.services.postgresql.superUser;
};
# grafana-database-password = {
# source-file = grafana-database-passwd-file;
# target-file = "/run/services/grafana/db.passwd";
# user = config.systemd.services.grafana.serviceConfig.User;
# };
# postgres-grafana-password = {
# source-file = grafana-database-passwd-file;
# target-file = "/run/services/postgres/db.passwd";
# user = config.services.postgresql.superUser;
# };
pricebot-auth-token = {
source-file =
config.fudo.secrets.files.service-secrets.nostromo."pricebot-auth.token";
@@ -118,13 +127,21 @@ in {
metrics.grafana = {
state-directory = "/state/services/grafana";
smtp.hostname = "mail.fudo.org";
database = {
user = "grafana";
password-file = host-secrets.grafana-database-password.target-file;
};
# database = {
# user = "grafana";
# password-file = host-secrets.grafana-database-password.target-file;
# };
ldap.base-dn = "dc=fudo,dc=org";
};
postgresql = {
state-directory = "/state/services/postgresql";
keytab = extractFudoKeytab {
realm = domain.gssapi-realm;
principals = [ "postgres/${host-fqdn}" ];
};
};
auth.kerberos.state-directory = "/state/services/heimdal-kdc";
};
@@ -135,18 +152,28 @@ in {
databases.grafana.users = config.instance.local-admins;
users.grafana = {
password-file = host-secrets.postgres-grafana-password.target-file;
databases.grafana = {
entity-access = {
"ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
"ALL SEQUENCES IN SCHEMA public" = "ALL PRIVILEGES";
};
};
};
# users.grafana = {
# password-file = host-secrets.postgres-grafana-password.target-file;
# databases.grafana = {
# entity-access = {
# "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
# "ALL SEQUENCES IN SCHEMA public" = "ALL PRIVILEGES";
# };
# };
# };
};
};
virtualisation = {
docker.enable = false;
podman = {
enable = true;
dockerSocket.enable = true;
autoPrune.enable = true;
};
arion.backend = "podman-socket";
};
imports = [ ./nostromo/factorio.nix ./nostromo/minecraft.nix ];
## Until I can figure out how to use one common host API, forget this
+1 -1
View File
@@ -168,7 +168,7 @@ in {
}"
(optionalString cfg.loadLatestSave "--start-server-load-latest")
(optionalString (cfg.mods != [ ])
"--mod-directory=${pkgs.factorio-utils.mkModDirDrv cfg.mods}")
"--mod-directory=${pkgs.factorio-utils.mkModDirDrv cfg.mods null}")
(optionalString (cfg.admins != [ ]) "--server-adminlist=${
pkgs.writeText "factorio-server-adminlist.json"
(builtins.toJSON cfg.admins)
+1 -49
View File
@@ -20,10 +20,6 @@ let
acme-copies = config.fudo.acme.host-domains.${hostname};
grafana-database-passwd-file =
pkgs.lib.passwd.stablerandom-passwd-file "grafana-database-password"
"grafana-database-password-${config.instance.build-seed}";
in {
imports = [
@@ -82,18 +78,6 @@ in {
target-file = "/run/openldap/ldap.keytab";
user = config.services.openldap.user;
};
grafana-database-password = {
source-file = grafana-database-passwd-file;
target-file = "/run/metrics/grafana/db.passwd";
user = config.systemd.services.grafana.serviceConfig.User;
};
postgres-grafana-password = {
source-file = grafana-database-passwd-file;
target-file = "/run/postgres-users/grafana.passwd";
user = config.services.postgresql.superUser;
};
};
acme.host-domains.${hostname} = {
@@ -144,25 +128,10 @@ in {
metrics = {
prometheus.state-directory = "/state/services/prometheus";
grafana = {
state-directory = "/state/services/grafana";
database = {
user = "grafana";
password-file =
host-secrets.grafana-database-password.target-file;
};
};
grafana.state-directory = "/state/services/grafana";
};
logging.loki.state-directory = "/state/services/loki";
# selby-forum = {
# enable = true;
# state-directory = "/state/services/selby-forum";
# legacy-forum-data = files.blobs."selby-forum-2021-12-14.clean";
# external-interface = "extif0";
# mail.host = "mail.fudo.org";
# };
};
# dns.state-directory = "/state/nsd";
@@ -209,23 +178,6 @@ in {
# };
# };
postgresql = {
databases.grafana.users = config.instance.local-admins;
users.grafana = {
password-file = host-secrets.postgres-grafana-password.target-file;
databases.grafana = {
access = "CONNECT";
entity-access = {
"ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
# "SELECT,INSERT,UPDATE,DELETE";
"ALL SEQUENCES IN SCHEMA public" = "ALL PRIVILEGES";
# "SELECT, UPDATE";
};
};
};
};
# git = {
# enable = true;
# hostname = "git.informis.land";
+110 -38
View File
@@ -22,9 +22,9 @@ let
host-certs = config.fudo.acme.host-domains.${hostname};
grafana-database-password = pkgs.lib.passwd.stablerandom-passwd-file
"grafana-database-password-${hostname}"
"grafana-database-password-${hostname}-${config.instance.build-seed}";
# grafana-database-password = pkgs.lib.passwd.stablerandom-passwd-file
# "grafana-database-password-${hostname}"
# "grafana-database-password-${hostname}-${config.instance.build-seed}";
in {
networking = {
@@ -54,11 +54,81 @@ in {
};
};
virtualisation = {
docker.enable = false;
podman = {
enable = true;
autoPrune.enable = true;
dockerSocket.enable = true;
};
oci-containers.backend = "podman";
arion.backend = "podman-socket";
};
services = {
# lemmy = {
# server.package = pkgs.unstable.lemmy-server;
# ui.package = pkgs.unstable.lemmy-ui;
# };
# lemmyDocker = {
# enable = true;
# hostname = "informis.land";
# site-name = "Informis Lemmy";
# version = "0.18.2";
# state-directory = "/state/services/lem";
# smtp-server = "smtp.informis.land:587";
# docker-images = {
# pictrs = "asonix/pictrs:0.4.0-rc.14";
# postgres = "postgres:15-alpine";
# };
# };
nginx = {
enable = true;
recommendedGzipSettings = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
# virtualHosts."lemmy.informis.land" = {
# enableACME = true;
# forceSSL = true;
# locations = {
# "/" = {
# proxyPass =
# "http://localhost:${toString config.services.lemmy.ui.port}";
# proxyWebsockets = true;
# extraConfig = ''
# proxy_set_header Host $host;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "Upgrade";
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# '';
# };
# "/api" = {
# proxyPass = "http://localhost:${
# toString config.services.lemmy.settings.port
# }";
# proxyWebsockets = true;
# extraConfig = ''
# proxy_set_header Host $host;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection "Upgrade";
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# '';
# };
# };
# };
};
};
environment.systemPackages = local-packages;
networking.firewall.allowedTCPPorts = [ 80 443 ];
security.acme.defaults.email = "viator@informis.land";
security.acme.defaults.email = "root@informis.land";
users = {
users.gituser = {
@@ -103,6 +173,8 @@ in {
fudo = {
hosts.procul.external-interfaces = [ "extif0" ];
# zones."informis.land".aliases.lemmy = "procul";
acme.host-domains.${hostname} = {
${host-fqdn} = {
admin-email = "admin@${domain-name}";
@@ -164,17 +236,17 @@ in {
user = "root";
};
grafana-postgres-password = {
source-file = grafana-database-password;
target-file = "/run/metrics/grafana/db.passwd";
user = config.systemd.services.grafana.serviceConfig.User;
};
# grafana-postgres-password = {
# source-file = grafana-database-password;
# target-file = "/run/metrics/grafana/db.passwd";
# user = config.systemd.services.grafana.serviceConfig.User;
# };
postgres-grafana-password = {
source-file = grafana-database-password;
target-file = "/run/postgres-users/grafana.passwd";
user = config.services.postgresql.superUser;
};
# postgres-grafana-password = {
# source-file = grafana-database-password;
# target-file = "/run/postgres-users/grafana.passwd";
# user = config.services.postgresql.superUser;
# };
};
client.dns = {
@@ -194,8 +266,6 @@ in {
allowed-networks = [ "1.1.1.1/32" "1.0.0.1/32" "localhost" "link-local" ];
};
services.mail-server = { state-directory = "/srv/mailserver"; };
# mail-server = {
# enable = true;
# debug = true;
@@ -252,27 +322,29 @@ in {
};
ldap.state-directory = "/state/services/ldap";
};
dns.zones."informis.land" = {
enable = true;
default-host = host-ipv4;
};
dns.zones."informis.land".enable = true;
postgresql = {
state-directory = "/state/services/postgresql";
state-directory = "/state/services/postgresql-15";
keytab = extractFudoHostKeytab {
inherit hostname;
realm = domain.gssapi-realm;
services = [ "postgres" ];
};
};
# lemmy = {
# enable = true;
# hostname = "lemmy.informis.land";
# };
logging.loki.state-directory = "/state/services/loki";
mail-server = { state-directory = "/srv/mailserver"; };
metrics = {
prometheus.state-directory = "/state/services/prometheus";
grafana = {
state-directory = "/state/services/grafana";
database = {
user = "grafana";
password-file = host-secrets.grafana-postgres-password.target-file;
};
# database = {
# user = "grafana";
# password-file = host-secrets.grafana-postgres-password.target-file;
# };
};
};
};
@@ -287,18 +359,18 @@ in {
# local-networks = local-networks;
users = {
grafana = {
password-file = host-secrets.postgres-grafana-password.target-file;
databases.grafana = {
access = "CONNECT";
entity-access = {
"ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
# "SELECT,INSERT,UPDATE,DELETE";
"ALL SEQUENCES IN SCHEMA public" = "ALL PRIVILEGES";
# "SELECT, UPDATE";
};
};
};
# grafana = {
# password-file = host-secrets.postgres-grafana-password.target-file;
# databases.grafana = {
# access = "CONNECT";
# entity-access = {
# "ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
# # "SELECT,INSERT,UPDATE,DELETE";
# "ALL SEQUENCES IN SCHEMA public" = "ALL PRIVILEGES";
# # "SELECT, UPDATE";
# };
# };
# };
gituser = {
password-file = host-secrets.postgres-gitea-password.target-file;
@@ -315,7 +387,7 @@ in {
};
databases = {
grafana.users = config.instance.local-admins;
# grafana.users = config.instance.local-admins;
git.users = config.instance.local-admins;
};
};
+18 -10
View File
@@ -14,24 +14,32 @@ in {
};
i18n.inputMethod = {
enabled = "fcitx5";
fcitx5.addons = with pkgs; [ fcitx5-chinese-addons fcitx5-rime ];
#enabled = "fcitx5";
#fcitx5.addons = with pkgs; [ fcitx5-chinese-addons fcitx5-rime ];
};
systemd.tmpfiles.rules = [ "d ${state-dir}/lib/cups 755 root root - -" ];
systemd.tmpfiles.rules = [
"d ${state-dir}/lib/cups 755 root root - -"
"d ${state-dir}/services/yggdrasil 700 root root - -"
];
fileSystems = {
"/var/lib/cups" = {
device = "${state-dir}/lib/cups";
options = [ "bind" ];
};
# "/var/lib/private/yggdrasil" = {
# device = "${state-dir}/services/yggdrasil";
# options = [ "bind" ];
# };
};
# fudo.adguard-dns-proxy = {
# enable = true;
# http.listen-ip = "10.0.0.108";
# dns.listen-port = 1053;
# local-domain-name = "sea.fudo.org";
# verbose = true;
# };
services = {
yggdrasil = {
enable = true;
persistentKeys = true;
group = "wheel";
};
};
}
+47 -11
View File
@@ -28,23 +28,59 @@ in {
'';
fudo = {
# minecraft-server = {
# enable = true;
# data-dir = "/state/services/minecraft";
# world-name = "TLS";
# motd = "Welcome to Jasper's TLS minecraft server.";
# game-mode = "survival";
# difficulty = 1;
# allow-cheats = true;
# allocated-memory = 16;
# port = 25555;
# query-port = 25556;
# rcon-port = 25557;
# world-seed = 2090763904884813452;
# };
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;
REPLand = {
allocated-memory = 8;
port = 25565;
query-port = 25566;
rcon-port = 25567;
};
tls = {
world-name = "TLS";
motd = "Welcome to Jasper's TLS minecraft world";
world-seed = 1613434103;
port = 25555;
query-port = 25556;
rcon-port = 25557;
game-mode = "survival";
difficulty = "easy";
allow-cheats = true;
pvp = false;
allocated-memory = 16;
package = pkgs.papermc-current;
};
# wof = {
# world-name = "WorldOfFun";
# # world-seed = 2059666523504992;
# world-seed = 2090763904884813452;
# port = 25568;
# query-port = 25569;
# rcon-port = 25570;
# difficulty = "medium";
# game-mode = "survival";
# motd = "Welcome to the World of Fun!";
# allow-cheats = true;
# allocated-memory = 16;
# pvp = false;
# };
};
};
};
+1 -1
View File
@@ -177,7 +177,7 @@ in {
}"
(optionalString cfg.loadLatestSave "--start-server-load-latest")
(optionalString (cfg.mods != [ ])
"--mod-directory=${pkgs.factorio-utils.mkModDirDrv cfg.mods}")
"--mod-directory=${pkgs.factorio-utils.mkModDirDrv cfg.mods null}")
(optionalString (cfg.admins != [ ]) "--server-adminlist=${
pkgs.writeText "factorio-server-adminlist.json"
(builtins.toJSON cfg.admins)
+263 -178
View File
@@ -14,206 +14,291 @@ let
pkgs.lib.passwd.stablerandom-passwd-file "zigbee2mqtt-passwd"
config.instance.build-seed;
teslaMateMqttPasswdFile =
pkgs.lib.passwd.stablerandom-passwd-file "tesla-mate-mqtt-passwd"
config.instance.build-seed;
nitenMqttPasswdFile =
pkgs.lib.passwd.stablerandom-passwd-file "niten-mqtt-passwd"
config.instance.build-seed;
nodeRedPasswdFile =
pkgs.lib.passwd.stablerandom-passwd-file "node-red-mqtt-passwd"
config.instance.build-seed;
teslaMatePort = 4400;
teslaGraphPort = 4401;
nodeRedPort = 1880;
host-secrets = config.fudo.secrets.host-secrets.${hostname};
host-passwds = config.fudo.secrets.files.service-passwords.${hostname};
in {
boot.kernel.sysctl = { "net.ipv4.ip_forward" = true; };
imports = [
(import ./wormhole0/home-assistant.nix {
homeAssistantImage = "ghcr.io/home-assistant/home-assistant:2023.9";
nodeRedImage = "nodered/node-red:3.1.0-14";
inherit nodeRedPort;
stateDirectory = "/state/services/arion-home-assistant";
})
];
networking = {
hostName = hostname;
config = {
boot.kernel.sysctl = { "net.ipv4.ip_forward" = true; };
firewall = { enable = false; };
networking = {
hostName = hostname;
defaultGateway = {
address = "10.0.0.1";
interface = "intif0";
};
firewall = { enable = false; };
nameservers = [ "10.0.0.1" ];
interfaces = {
intif0 = {
useDHCP = false;
ipv4 = {
addresses = [{
address = primary-ip;
prefixLength = 16;
}];
};
defaultGateway = {
address = "10.0.0.1";
interface = "intif0";
};
wormif0.useDHCP = true;
nameservers = [ "10.0.0.1" ];
wlp2s0.useDHCP = false;
interfaces = {
intif0 = {
useDHCP = false;
ipv4 = {
addresses = [{
address = primary-ip;
prefixLength = 16;
}];
};
};
wormif0.useDHCP = true;
wlp2s0.useDHCP = false;
};
dhcpcd.extraConfig = concatStringsSep "\n" [ "nogateway" ];
};
dhcpcd.extraConfig = concatStringsSep "\n" [ "nogateway" ];
};
fudo.services.mqtt = {
enable = true;
state-directory = "${state-dir}/services/mosquitto";
private = {
fudo.services.mqtt = {
enable = true;
users = {
zigbee2mqtt = {
password-file = zigbee2mqtt-passwd-file;
acl = [ "readwrite #" ];
};
home-assistant = {
password-file = host-passwds.mosquitto-home-assistant;
acl = [ "readwrite #" ];
state-directory = "${state-dir}/services/mosquitto";
private = {
enable = true;
users = {
zigbee2mqtt = {
password-file = zigbee2mqtt-passwd-file;
acl = [ "readwrite #" ];
};
home-assistant = {
password-file = host-passwds.mosquitto-home-assistant;
acl = [ "readwrite #" ];
};
tesla-mate = {
password-file = teslaMateMqttPasswdFile;
acl = [ "readwrite teslamate/cars/#" ];
};
niten = {
password-file = nitenMqttPasswdFile;
acl = [ "readwrite #" ];
};
node-red = {
password-file = nodeRedPasswdFile;
acl = [ "readwrite #" ];
};
};
};
};
};
systemd = {
systemd = {
services = {
wormhole-route = {
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" ];
serviceConfig = {
ExecStart =
"${pkgs.iproute2}/bin/ip route add 192.168.86.0/24 dev wormif0";
ExecStop =
"${pkgs.iproute2}/bin/ip route del 192.168.86.0/24 dev wormif0";
RemainAfterExit = true;
};
};
};
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"
"L /etc/adjtime - - - - ${state-dir}/etc/adjtime"
"d /state/services 0711 root root - -"
"d ${zigbee2mqtt-statedir} 0700 ${zigbee2mqtt-user} - - -"
];
};
users.groups = let
zigbee2mqtt-user = config.systemd.services.zigbee2mqtt.serviceConfig.User;
in { dialout.members = [ zigbee2mqtt-user ]; };
services = {
wormhole-route = {
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" ];
serviceConfig = {
ExecStart =
"${pkgs.iproute2}/bin/ip route add 192.168.86.0/24 dev wormif0";
ExecStop =
"${pkgs.iproute2}/bin/ip route del 192.168.86.0/24 dev wormif0";
RemainAfterExit = true;
openssh.hostKeys = [
{
path = "${state-dir}/ssh/ssh_host_rsa_key";
type = "rsa";
bits = 4096;
}
{
path = "${state-dir}/ssh/ssh_host_ed25519_key";
type = "ed25519";
}
];
nginx = {
enable = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedGzipSettings = true;
virtualHosts = {
"home-assist.sea.fudo.org" = {
locations."/" = {
proxyPass = "http://localhost:${toString home-assistant-port}";
proxyWebsockets = true;
};
};
"node-red.sea.fudo.org" = {
locations."/" = {
proxyPass = "http://localhost:${toString nodeRedPort}";
proxyWebsockets = true;
};
};
"tesla-mate.sea.fudo.org" = {
locations."/" = {
proxyPass = "http://localhost:${toString teslaMatePort}";
proxyWebsockets = true;
};
};
"tesla-graph.sea.fudo.org" = {
locations."/" = {
proxyPass = "http://localhost:${toString teslaGraphPort}";
proxyWebsockets = true;
};
};
};
};
# 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 #" ];
# # };
# # wallfly = {
# # passwordFile = host-secrets.mosquitto-wallfly-passwd.target-file;
# # acl = [ "readwrite homeassistant/binary_sensor/#" ];
# # };
# };
# }];
# };
zigbee2mqtt = {
enable = true;
dataDir = zigbee2mqtt-statedir;
package = pkgs.pkgsUnstable.zigbee2mqtt;
settings = {
homeassistant = true;
permit_join = true;
serial.port = "/dev/ttyUSB0";
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
# described https://www.zigbee2mqtt.io/guide/configuration/mqtt.html#server-connection
# Weird, though.
};
advanced.log_level = "debug";
};
};
avahi = {
enable = true;
reflector = true;
interfaces = [ "intif0" "worm0" ];
};
teslaMateContainer = {
enable = true;
mqtt = {
host = config.fudo.services.mqtt.mqtt-hostname;
port = config.fudo.services.mqtt.private.port;
user = "tesla-mate";
password = readFile teslaMateMqttPasswdFile;
};
port = teslaMatePort;
grafana-port = teslaGraphPort;
state-directory = "/state/services/tesla-mate";
};
};
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"
"L /etc/adjtime - - - - ${state-dir}/etc/adjtime"
"d /state/services 0711 root root - -"
"d ${zigbee2mqtt-statedir} 0700 ${zigbee2mqtt-user} - - -"
];
virtualisation = {
docker = {
enable = false;
#enableOnBoot = true;
#autoPrune.enable = true;
};
podman = {
enable = true;
dockerSocket.enable = true;
autoPrune.enable = true;
};
arion.backend = "podman-socket";
# oci-containers = {
# backend = "podman";
# containers = {
# home-assistant = {
# image = "homeassistant/home-assistant:stable";
# autoStart = true;
# environment.TZ = config.time.timeZone;
# #ports = [ "${toString home-assistant-port}:8123" ];
# volumes = [ "/state/services/home-assistant:/config" ];
# extraOptions = [ "--network=host" ];
# };
# };
# };
};
security.sudo.extraConfig = ''
# Due to rollback, sudo will lecture after every reboot
Defaults lecture = never
'';
};
users.groups = let
zigbee2mqtt-user = config.systemd.services.zigbee2mqtt.serviceConfig.User;
in { dialout.members = [ zigbee2mqtt-user ]; };
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";
}
];
nginx = {
enable = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedGzipSettings = true;
virtualHosts."home-assist.sea.fudo.org" = {
locations."/" = {
proxyPass = "http://localhost:${toString home-assistant-port}";
proxyWebsockets = true;
};
};
};
# 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 #" ];
# # };
# # wallfly = {
# # passwordFile = host-secrets.mosquitto-wallfly-passwd.target-file;
# # acl = [ "readwrite homeassistant/binary_sensor/#" ];
# # };
# };
# }];
# };
zigbee2mqtt = {
enable = true;
dataDir = zigbee2mqtt-statedir;
package = pkgs.pkgsUnstable.zigbee2mqtt;
settings = {
homeassistant = true;
permit_join = true;
serial.port = "/dev/ttyUSB0";
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
# described https://www.zigbee2mqtt.io/guide/configuration/mqtt.html#server-connection
# Weird, though.
};
advanced.log_level = "debug";
};
};
avahi = {
enable = true;
reflector = true;
interfaces = [ "intif0" "worm0" ];
};
};
virtualisation = {
docker = {
enable = true;
enableOnBoot = true;
autoPrune.enable = true;
};
oci-containers = {
backend = "docker";
containers = {
home-assistant = {
image = "homeassistant/home-assistant:stable";
autoStart = true;
environment.TZ = config.time.timeZone;
#ports = [ "${toString home-assistant-port}:8123" ];
volumes = [ "/state/services/home-assistant:/config" ];
extraOptions = [ "--network=host" ];
};
};
};
};
security.sudo.extraConfig = ''
# Due to rollback, sudo will lecture after every reboot
Defaults lecture = never
'';
}
@@ -0,0 +1,75 @@
{ homeAssistantImage, nodeRedImage, nodeRedPort ? 1880, stateDirectory, ... }:
{ config, lib, pkgs, ... }:
with lib;
let
homeAssistantUid = 730;
nodeRedUid = 731;
in {
config = {
users = {
users = {
home-assistant = {
isSystemUser = true;
group = "home-assistant";
uid = homeAssistantUid;
};
home-assistant-node-red = {
isSystemUser = true;
group = "home-assistant";
uid = nodeRedUid;
};
};
groups.home-assistant = {
members = [ "home-assistant" ] ++ config.instance.local-admins;
};
};
systemd = {
services.arion-home-assistant = {
requires = [ "podman.service" "mosquitto.service" ];
after = [
"podman.service"
"network-online.target"
"fudo-secrets.target"
"mosquitto.service"
];
};
tmpfiles.rules = [
"d ${stateDirectory}/config 0770 ${
toString homeAssistantUid
} home-assistant - -"
"d ${stateDirectory}/node-red 0700 ${toString nodeRedUid} root - -"
];
};
virtualisation.arion.projects.home-assistant.settings = let
image = { pkgs, ... }: {
project.name = "home-assistant";
services = {
home-assistant.service = {
image = homeAssistantImage;
restart = "always";
volumes = [
"${stateDirectory}/config:/config"
"/etc/localtime:/etc/localtime:ro"
];
user = "${toString homeAssistantUid}:${toString homeAssistantUid}";
network_mode = "host";
};
node-red.service = {
image = nodeRedImage;
restart = "always";
volumes = [ "${stateDirectory}/node-red:/data" ];
ports = [ "${toString nodeRedPort}:1880" ];
depends_on = [ "home-assistant" ];
environment.TZ = config.time.timeZone;
user = "${toString nodeRedUid}:${toString nodeRedUid}";
};
};
};
in { imports = [ image ]; };
};
}
+2 -2
View File
@@ -15,8 +15,8 @@ in {
};
i18n.inputMethod = {
enabled = "fcitx5";
fcitx5.addons = with pkgs; [ fcitx5-chinese-addons fcitx5-rime ];
#enabled = "fcitx5";
# fcitx5.addons = with pkgs; [ fcitx5-chinese-addons fcitx5-rime ];
};
systemd.tmpfiles.rules = [
+12 -25
View File
@@ -1,7 +1,6 @@
{ config, lib, pkgs, ... }:
with lib;
{
with lib; {
config = let
local-host = config.instance.hostname;
local-domain = config.fudo.hosts.${local-host}.domain;
@@ -13,8 +12,8 @@ with lib;
host-user-list = host.local-users;
domain-user-list = config.fudo.domains."${local-domain}".local-users;
site-user-list = config.fudo.sites."${local-site}".local-users;
all-users =
getAttrs (host-user-list ++ domain-user-list ++ site-user-list) config.fudo.users;
all-users = getAttrs (host-user-list ++ domain-user-list ++ site-user-list)
config.fudo.users;
host-admin-list = host.local-admins;
domain-admin-list = config.fudo.domains."${local-domain}".local-admins;
@@ -26,38 +25,26 @@ with lib;
site-group-list = config.fudo.sites."${local-site}".local-groups;
local-groups =
getAttrs (host-group-list ++ domain-group-list ++ site-group-list)
config.fudo.groups;
config.fudo.groups;
local-hosts =
filterAttrs (host: hostOpts: hostOpts.site == local-site) config.fudo.hosts;
local-hosts = filterAttrs (host: hostOpts: hostOpts.site == local-site)
config.fudo.hosts;
local-networks =
host.local-networks ++
config.fudo.domains.${local-domain}.local-networks ++
config.fudo.sites.${local-site}.local-networks;
local-networks = host.local-networks
++ config.fudo.domains."${local-domain}".local-networks
++ config.fudo.sites."${local-site}".local-networks;
local-profile = host.profile;
host-fqdn = "${config.instance.hostname}.${local-domain}";
local-users =
if (host.hardened) then
getAttrs local-admins all-users
else all-users;
if (host.hardened) then getAttrs local-admins all-users else all-users;
in {
instance = {
inherit
host-fqdn
local-domain
local-site
local-users
local-admins
local-groups
local-hosts
local-profile
local-networks
local-zone;
inherit host-fqdn local-domain local-site local-users local-admins
local-groups local-hosts local-profile local-networks local-zone;
};
};
}
+17 -15
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;
@@ -55,20 +55,22 @@ in {
openssh = {
enable = true;
startWhenNeeded = true;
useDns = true;
permitRootLogin = "prohibit-password";
# 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"
"curve25519-sha256@libssh.org"
"diffie-hellman-group-exchange-sha256"
];
settings = {
UseDns = true;
PermitRootLogin = "prohibit-password";
# 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"
# "curve25519-sha256@libssh.org"
# "diffie-hellman-group-exchange-sha256"
# ];
};
};
fail2ban =
+6 -2
View File
@@ -43,6 +43,7 @@ in {
paths."${hostname}-keytab-watcher" = {
wantedBy = [ "default.target" ];
description = "Watch host keytab for changes.";
after = [ "fudo-secrets.target" ];
pathConfig = {
PathChanged = host-keytab;
Unit = "${hostname}-keytab-watcher.service";
@@ -55,14 +56,17 @@ in {
"When host keytab is available or changed, activate copy job.";
path = with pkgs; [ systemd ];
serviceConfig = { Type = "oneshot"; };
after = [ "fudo-secrets.target" ];
script = "systemctl restart ${hostname}-copy-keytab.service";
};
"${hostname}-copy-keytab" = {
description =
"Copy the host krb5.keytab into place once it's available.";
after = [ "fudo-secrets.target" "fudo-secret-host-keytab.service" ];
wantedBy = [ "default.target" ];
serviceConfig = {
Type = "simple";
Type = "oneshot";
RemainAfterExit = true;
ExecStart = pkgs.writeShellScript "${hostname}-copy-keytab.sh" ''
[ -f ${host-keytab} ] || exit 1
@@ -79,7 +83,7 @@ in {
};
};
fudo.secrets.host-secrets.${hostname}.host-keytab =
fudo.secrets.host-secrets."${hostname}".host-keytab =
mkIf (keytab-file != null) {
source-file = keytab-file;
target-file = "/run/kerberos/krb5.keytab";
+338
View File
@@ -0,0 +1,338 @@
{ config, lib, pkgs, ... }@toplevel:
with lib;
let
hostname = config.instance.hostname;
cfg = config.fudo.services.authoritative-dns;
hostSecrets = config.fudo.secrets.host-secrets."${hostname}";
domainName = config.instance.local-domain;
# domain = config.fudo.domains."${domainName}";
# primaryNameserver = domain.primaryNameserver;
# isPrimaryNameserver = primary-nameserver == hostname;
zoneKeySecret = zone: "${zone}-ksk";
nameserverOpts = { name, ... }: {
options = with types; {
hostname = mkOption {
type = str;
description = "Hostname of the external nameserver.";
default = name;
};
ipv4-address = mkOption {
type = nullOr str;
description = "Host ipv4 address of the external nameserver.";
default = null;
};
ipv6-address = mkOption {
type = nullOr str;
description = "Host ipv6 address of the external nameserver.";
default = null;
};
authoritative-hostname = mkOption {
type = nullOr str;
description = "Authoritative hostname of this nameserver.";
default = null;
};
description = mkOption {
type = str;
description = "Description of the external nameserver.";
};
};
};
networkHostOpts = {
options = with types; {
hostname = mkOption {
type = str;
description = "Hostname.";
};
ipv4-address = mkOption {
type = nullOr str;
description = "The V4 IP of a given host, if any.";
default = null;
};
ipv6-address = mkOption {
type = nullOr str;
description = "The V6 IP of a given host, if any.";
default = null;
};
mac-address = mkOption {
type = nullOr str;
description =
"The MAC address of a given host, if desired for IP reservation.";
default = null;
};
description = mkOption {
type = nullOr str;
description = "Description of the host.";
default = null;
};
sshfp-records = mkOption {
type = listOf str;
description = "List of SSHFP records for this host.";
default = [ ];
};
};
};
zoneOpts = { name, ... }:
let zoneName = name;
in {
options = with types; {
enable = mkOption {
type = bool;
description = "Enable ${zone-name} zone on the local nameserver.";
default = true;
};
default-host = mkOption {
type = nullOr (submodule networkHostOpts);
description =
"Host which will respond to requests for the base domain.";
default = null;
};
external-nameservers = mkOption {
type = listOf (submodule nameserverOpts);
description = "List of external nameserver clauses.";
default = [ ];
};
domain = mkOption {
type = str;
description = "Domain which this zone serves.";
default = zoneName;
};
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."${zoneName}";
};
};
};
in {
options.fudo.services.authoritative-dns = with types; {
enable = mkEnableOption "Enable Authoritative DNS server.";
zones = mkOption {
type = attrsOf (submodule zoneOpts);
description = "Map of served zone to extra zone details.";
default = { };
};
nameservers = {
primary = mkOption {
type = str;
description = "Hostname of the primary nameserver.";
};
secondary = mkOption {
type = listOf str;
description = "List of internal secondary nameservers.";
default = [ ];
};
};
state-directory = mkOption {
type = str;
description =
"Directory at which to store DNS state data, including keys.";
};
};
config = {
fudo = {
secrets.host-secrets."${hostname}" = mkIf cfg.enable (mapAttrs'
(zone: zoneCfg:
nameValuePair (zoneKeySecret zone) {
source-file = zoneCfg.ksk.private-key;
target-file = "/run/nsd/${baseNameOf zoneCfg.ksk.private-key}";
user = config.fudo.nsd.user;
}) (filterAttrs (_: zoneCfg: zoneCfg.ksk != null) cfg.zones));
zones = mapAttrs (zone-name: zoneCfg:
let
domainName = zoneCfg.domain;
domain = config.fudo.domains."${domainName}";
makeSrvRecord = port: host: { inherit port host; };
# servedDomain = domain.primary-nameserver != null;
# primaryNameserver = domain.primary-nameserver;
isPrimaryNameserver = hostname == cfg.nameservers.primary;
internalNameserverHostnames = [ cfg.nameservers.primary ]
++ cfg.nameservers.secondary;
getNsDeets = hostname:
let hostDomain = config.fudo.hosts."${hostname}".domain;
in {
ipv4-address = pkgs.lib.network.host-ipv4 config hostname;
ipv6-address = pkgs.lib.network.host-ipv6 config hostname;
description =
"${domainName} nameserver ${hostname}.${hostDomain}.";
};
nameserverDeets = let
internalNameservers = map getNsDeets internalNameserverHostnames;
in internalNameservers ++ zoneCfg.external-nameservers;
hasAuthHostname = nsHost: nsOpts:
(hasAttr "authoritative-hostname" nsOpts)
&& (nsOpts.authoritative-hostname != null);
allNameservers = listToAttrs
(imap1 (i: nsOpts: nameValuePair "ns${toString i}" nsOpts)
nameserverDeets);
nameserverAliases =
mapAttrs (hostname: opts: "${opts.authoritative-hostname}.")
(filterAttrs hasAuthHostname allNameservers);
nameserverHosts = mapAttrs (hostname: opts: {
inherit (opts) ipv4-address ipv6-address description;
}) (filterAttrs (hostname: opts: !hasAuthHostname hostname opts)
allNameservers);
dnsSrvRecords = let
nameserverSrvRecords = mapAttrsToList (hostname: hostOpts:
let
targetHost = if (hasAuthHostname hostname hostOpts) then
"${hostOpts.authoritative-hostname}"
else
"${hostname}.${domainName}";
in makeSrvRecord 53 targetHost) allNameservers;
in {
tcp.domain = nameserverSrvRecords;
udp.domain = nameserverSrvRecords;
};
mailSrvRecords = mkIf (!isNull domain.primary-mailserver) (let
mailDomainName =
config.fudo.hosts."${domain.primary-mailserver}".domain;
in {
tcp = {
smtp = [{
host = "smtp.${mailDomainName}";
port = 25;
}];
imap = [{
host = "imap.${mailDomainName}";
port = 143;
}];
imaps = [{
host = "imap.${mailDomainName}";
port = 993;
}];
submission = [{
host = "smtp.${mailDomainName}";
port = 587;
}];
submissions = [{
host = "smtp.${mailDomainName}";
port = 465;
}];
};
udp = {
smtp = [{
host = "smtp.${mailDomainName}";
port = 25;
}];
submission = [{
host = "smtp.${mailDomainName}";
port = 587;
}];
};
});
in {
gssapi-realm = mkIf (!isNull domain.gssapi-realm) domain.gssapi-realm;
hosts = nameserverHosts;
# Don't add mailservers: remember SSL!
aliases = nameserverAliases;
mx = optional (!isNull domain.primary-mailserver) (let
mailserverDomain =
config.fudo.hosts."${domain.primary-mailserver}".domain;
in "smtp.${mailserverDomain}");
dmarc-report-address = mkIf (!isNull domain.primary-mailserver)
"dmarc-report@${domainName}";
nameservers = let
directExternal = attrValues nameserverAliases;
internal = map (hostname: "${hostname}.${domainName}.")
(attrNames nameserverHosts);
in internal ++ directExternal;
srv-records = dnsSrvRecords // mailSrvRecords;
verbatim-dns-records = mkIf (zoneCfg.ksk != null) [
(readFile zoneCfg.ksk.public-key)
(readFile zoneCfg.ksk.ds)
];
}) cfg.zones;
};
services = {
authoritative-dns = {
enable = cfg.enable;
identity = "${hostname}.${domainName}";
listen-ips =
optionals cfg.enable (pkgs.lib.network.host-ips config hostname);
state-directory = cfg.state-directory;
timestamp = toString config.instance.build-timestamp;
domains = mapAttrs' (zoneName: zoneCfg:
nameValuePair zoneCfg.domain {
ksk.key-file = mkIf (hasAttr (zoneKeySecret zoneName) hostSecrets)
hostSecrets."${zoneKeySecret zoneName}".target-file;
zone = let baseZone = config.fudo.zones."${zoneName}";
in baseZone // {
# FIXME: what's up?
# default-host = baseZone.hosts."${zoneCfg.default-host}";
};
}) cfg.zones;
};
};
};
}
+5 -5
View File
@@ -110,9 +110,9 @@ in {
required-services = [ "fudo-passwords.target" ];
users = {
${database-powerdns-user} = {
"${database-powerdns-user}" = {
password-file = host-secrets.database-powerdns-passwd.target-file;
databases.${database-name} = {
databases."${database-name}" = {
access = "CONNECT";
entity-access = {
"ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE";
@@ -120,9 +120,9 @@ in {
};
};
};
${database-backplane-user} = {
"${database-backplane-user}" = {
password-file = host-secrets.database-backplane-passwd.target-file;
databases.${database-name} = {
databases."${database-name}" = {
access = "CONNECT";
entity-access = {
"ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE";
@@ -132,7 +132,7 @@ in {
};
};
databases.${database-name}.users = config.instance.local-admins;
databases."${database-name}".users = config.instance.local-admins;
};
backplane = {
+9 -29
View File
@@ -185,39 +185,16 @@ in {
udp.domain = nameserver-srv-records;
};
# # TODO: move this to a mail service
# mail-srv-records = optionalAttrs (domain.primary-mailserver != null) {
# tcp = let
# mailserver-domain =
# config.fudo.hosts.${domain.primary-mailserver}.domain;
# fqdn = "mail.${mailserver-domain}";
# in {
# smtp = [ (make-srv-record 25 fqdn) ];
# submission = [ (make-srv-record 587 fqdn) ];
# imap = [ (make-srv-record 143 fqdn) ];
# imaps = [ (make-srv-record 993 fqdn) ];
# pop3 = [ (make-srv-record 110 fqdn) ];
# pop3s = [ (make-srv-record 995 fqdn) ];
# };
# };
in {
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;
in mailserver-deets domain.primary-nameserver);
};
hosts = nameserver-hosts;
aliases = nameserver-aliases;
mx = optional (domain.primary-mailserver != null) (let
mail-domain-name =
config.fudo.hosts.${domain.primary-mailserver}.domain;
config.fudo.hosts."${domain.primary-mailserver}".domain;
in "mail.${mail-domain-name}");
dmarc-report-address = "dmarc-report@${domain-name}";
@@ -230,9 +207,10 @@ in {
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)
verbatim-dns-records = let pthru = o: trace o o;
in mkIf (zone-cfg.ksk != null) [
(pthru (readFile zone-cfg.ksk.public-key))
(pthru (readFile zone-cfg.ksk.ds))
];
}) cfg.zones;
@@ -248,7 +226,9 @@ in {
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};
zone-definition = config.fudo.zones."${zone-name}" // {
inherit (zone-cfg) default-host;
};
}) cfg.zones;
};
};
+7 -6
View File
@@ -106,11 +106,12 @@ in {
"--realm=${realm}"
"--verbose"
];
in pkgs.writeShellScript "heimdal-kdc-initialize.sh" ''
${init-db-cmd}
chown ${krb-user}:${krb-group} ${db}
chmod 0700 ${db}
'';
script = pkgs.writeShellScript "heimdal-kdc-initialize.sh" ''
chown ${krb-user}:${krb-group} ${db}
chmod 0700 ${db}
${init-db-cmd}
'';
in "+${script}";
};
};
heimdal-kdc = mkIf kerberos-master {
@@ -240,7 +241,7 @@ in {
};
};
zones.${zone-name} = let
zones."${zone-name}" = let
make-srv-record = port: hostname: {
port = port;
host = hostname;
+108
View File
@@ -0,0 +1,108 @@
{ config, lib, pkgs, ... }:
with lib;
let
lemmyDbPasswd = pkgs.lib.passwd.stablerandom-passwd-file "lemmy-server-passwd"
"lemmy-server-${config.instance.build-seed}";
cfg = config.fudo.services.lemmy;
hostname = config.instance.hostname;
domainName = config.fudo.hosts."${hostname}".domain;
postgresqlServer = config.fudo.domains."${domainName}".postgresql-server;
postgresqlFqdn = pkgs.lib.getHostFqdn postgresqlServer;
isPostgresServer = hostname == postgresqlServer;
hostSecrets = config.fudo.secrets.host-secrets."${hostname}";
in {
options.fudo.services.lemmy = with types; {
enable = mkEnableOption "Enable lemmy server.";
hostname = mkOption {
type = str;
description = "Hostname at which this server will be reachable.";
};
port = mkOption {
type = port;
description = "Port on which to listen for requests";
default = 8536;
};
listen-ip = mkOption {
type = str;
description = "IP on which to listen for incoming requests.";
default = "0.0.0.0";
};
};
config = mkIf cfg.enable {
fudo = {
secrets.host-secrets."${hostname}" = {
dbLemmyPasswd = mkIf isPostgresServer {
source-file = lemmyDbPasswd;
target-file = "/run/postgres/lemmy.passwd";
user = config.systemd.services.postgresql.serviceConfig.User;
};
lemmyEnv = mkIf cfg.enable {
source-file = pkgs.writeText "lemmy.env" ''
LEMMY_DATABASE_URL=postgres:///lemmy:${
readFile lemmyDbPasswd
}@${postgresqlFqdn}:5432/lemmy
'';
target-file = "/run/lemmy/env";
};
};
postgresql = mkIf isPostgresServer {
databases.lemmy.users = config.instance.local-admins;
users.lemmy = {
password-file = hostSecrets.dbLemmyPasswd.target-file;
databases.lemmy = {
access = "CONNECT";
entity-access = {
"ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
"ALL SEQUENCES IN SCHEMA public" = "ALL PRIVILEGES";
};
};
};
};
};
systemd.services.lemmy = {
requires = [ "fudo-secret-lemmyEnv.service" ];
after = [ "fudo-secret-lemmyEnv.service" ];
environment.LEMMY_DATABASE_URL = mkForce null;
serviceConfig = mkIf cfg.enable {
LoadCredential = [ "env:${hostSecrets.lemmyEnv.target-file}" ];
EnvironmentFile = "$$CREDENTIALS_DIRECTORY/env";
};
};
services.lemmy = mkIf cfg.enable {
enable = true;
settings = {
hostname = cfg.hostname;
federation.enabled = true;
captcha.enabled = true;
database = {
user = "lemmy";
host = pkgs.lib.getHostFqdn postgresqlServer;
database = "lemmy";
password = readFile lemmyDbPasswd;
};
bind = cfg.listen-ip;
port = cfg.port;
};
};
networking.firewall = {
allowedTCPPorts = [ cfg.port ];
allowedUDPPorts = [ cfg.port ];
};
};
}
+78 -74
View File
@@ -6,15 +6,14 @@ let
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}";
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;
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;
@@ -60,9 +59,7 @@ in {
};
groups = {
${cfg.promtail.user}.members = [ cfg.promtail.user ];
systemd-journal = {
members = [ cfg.promtail.user ];
};
systemd-journal = { members = [ cfg.promtail.user ]; };
};
};
@@ -74,14 +71,14 @@ in {
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";
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 = {
@@ -89,15 +86,16 @@ in {
enable = true;
virtualHosts."log-aggregator.${domain-name}" = {
enableACME = ! is-private-network;
forceSSL = ! is-private-network;
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;
extraConfig = let local-networks = config.instance.local-networks;
in "${optionalString ((length local-networks) > 0)
(concatStringsSep "\n"
(map (network: "allow ${network};") local-networks)) + "\ndeny all;"}";
(concatStringsSep "\n"
(map (network: "allow ${network};") local-networks)) + ''
deny all;''}";
};
};
};
@@ -131,23 +129,23 @@ in {
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";
};
}
];
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";
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";
};
@@ -169,35 +167,41 @@ in {
};
};
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} - - -"
]);
systemd = let
lokiUser = config.services.loki.user;
statedir = cfg.loki.state-directory;
in {
tmpfiles.rules = optionals isLogAggregator [
"d ${statedir} 0700 ${lokiUser} - - -"
"d ${statedir}/boltdb-shipper 0700 ${lokiUser} - - -"
"d ${statedir}/boltdb-shipper/active 0700 ${lokiUser} - - -"
"d ${statedir}/boltdb-shipper/cache 0700 ${lokiUser} - - -"
"d ${statedir}/chunks 0700 ${lokiUser} - - -"
"d ${statedir}/compactor 0700 ${lokiUser} - - -"
];
services.promtail = let
scheme = if is-private-network then "http" else "https";
services = {
loki.serviceConfig = mkIf isLogAggregator {
ExecStartPre =
"+${pkgs.coreutils}/bin/chown -R ${lokiUser}:root ${statedir}";
};
loki-host = host-fqdn domain.log-aggregator;
promtail = let
scheme = if is-private-network then "http" else "https";
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 = [
{
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";
@@ -210,20 +214,20 @@ in {
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;
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;
};
};
};
};
+24 -19
View File
@@ -14,6 +14,8 @@ let
mailserver-fqdn = "${mailserver-host}.${mailserver-domain-name}";
hasMailServer = mailserver-host != null;
isMailServer = hostname == mailserver-host;
isLocalMailserver = domain-name == mailserver-domain-name;
@@ -32,7 +34,7 @@ in {
};
};
config = {
config = mkIf hasMailServer {
services.nginx = mkIf (isMailServer && metricsEnabled) {
enable = true;
recommendedOptimisation = true;
@@ -102,27 +104,18 @@ in {
srv-record = host: port: [{ inherit host 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-fqdn}."; };
srv-records.tcp = {
pop3 = srv-record mailserver-fqdn 110;
pop3s = srv-record mailserver-fqdn 995;
# srv-records.tcp = {
# pop3 = srv-record mailserver-fqdn 110;
# pop3s = srv-record mailserver-fqdn 995;
imap = srv-record mailserver-fqdn 143;
imaps = srv-record mailserver-fqdn 993;
# imap = srv-record mailserver-fqdn 143;
# imaps = srv-record mailserver-fqdn 993;
smtp = srv-record mailserver-fqdn 25;
submission = srv-record mailserver-fqdn 587;
};
# smtp = srv-record mailserver-fqdn 25;
# submission = srv-record mailserver-fqdn 587;
# };
metric-records = mkIf metricsEnabled
(genAttrs [ "dovecot" "postfix" "rspamd" ]
@@ -168,7 +161,19 @@ in {
mail-directory = "${cfg.state-directory}/mail";
state-directory = "${cfg.state-directory}/state";
trusted-networks = config.instance.local-networks;
trusted-networks = let
wrapIpv6 = net:
let elems = builtins.split "/" net;
in "[${elemAt elems 0}]/${elemAt elems 2}";
in map (network:
if (builtins.match ".*::.*" network) != null then
wrapIpv6 network
else
network) config.instance.local-networks;
user-aliases = genAttrs (attrNames config.fudo.users)
(username: config.fudo.users."${username}".email-aliases);
alias-users = genAttrs [
"root"
+84 -49
View File
@@ -3,10 +3,10 @@
with lib;
let
hostname = config.instance.hostname;
domain-name = config.fudo.hosts.${hostname}.domain;
domain = config.fudo.domains.${domain-name};
domain-name = config.fudo.hosts."${hostname}".domain;
domain = config.fudo.domains."${domain-name}";
host-secrets = config.fudo.secrets.host-secrets.${hostname};
host-secrets = config.fudo.secrets.host-secrets."${hostname}";
notEmpty = lst: (length lst) > 0;
@@ -45,28 +45,37 @@ let
grafana-smtp-password-file =
pkgs.lib.passwd.stablerandom-passwd-file "grafana-smtp-passwd"
"grafana-smtp-passwd-${config.instance.build-seed}";
config.instance.build-seed;
grafana-auth-password-file =
pkgs.lib.passwd.stablerandom-passwd-file "grafana-auth-passwd"
"grafana-auth-passwd-${config.instance.build-seed}";
config.instance.build-seed;
grafana-admin-password-file =
pkgs.lib.passwd.stablerandom-passwd-file "grafana-admin-passwd"
"grafana-admin-passwd-${config.instance.build-seed}";
config.instance.build-seed;
grafana-secret-key-file =
pkgs.lib.passwd.stablerandom-passwd-file "grafana-secret-key"
"grafana-secret-key-${config.instance.build-seed}";
config.instance.build-seed;
is-private-network = let site-name = config.fudo.hosts.${hostname}.site;
in config.fudo.sites.${site-name}.local-gateway != null;
grafana-database-password-file =
pkgs.lib.passwd.stablerandom-passwd-file "grafana-database-postgres"
config.instance.build-seed;
site = let site-name = config.fudo.hosts."${hostname}".site;
in config.fudo.sites."${site-name}";
is-private-network = site.local-gateway != null;
domainToBaseDn = domain:
concatStringsSep "," (map (el: "dc=${el}") (splitString "." domain));
ldapEnabled = domain.ldap-servers != [ ];
isPostgresServer = config.instance.hostname == domain.postgresql-server;
postgresServer = pkgs.lib.getHostFqdn domain.postgresql-server;
in {
options.fudo.services.metrics = with types; {
prometheus = {
@@ -118,28 +127,28 @@ in {
};
};
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";
};
};
# 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;
@@ -152,14 +161,14 @@ in {
config = mkIf metricsEnabled {
fudo = {
system-users = {
${grafana-cfg.smtp.username} = {
"${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-user}" = mkIf ((domain.ldap-servers != [ ])
&& (grafana-cfg.ldap.bind-passwd == null)) {
description = "Grafana Authentication Reader";
ldap-hashed-password =
@@ -168,31 +177,43 @@ in {
};
};
secrets.host-secrets = mkIf metricsMonitor
(let grafana-user = config.systemd.services.grafana.serviceConfig.User;
secrets.host-secrets =
let grafana-user = config.systemd.services.grafana.serviceConfig.User;
in {
${hostname} = {
grafana-smtp-password = {
"${hostname}" = {
grafana-smtp-password = mkIf metricsMonitor {
source-file = grafana-smtp-password-file;
target-file = "/run/metrics/grafana/smtp.passwd";
user = grafana-user;
};
grafana-admin-password = {
grafana-admin-password = mkIf metricsMonitor {
source-file = grafana-admin-password-file;
target-file = "/run/metrics/grafana/admin.passwd";
user = grafana-user;
};
grafana-secret-key = {
grafana-secret-key = mkIf metricsMonitor {
source-file = grafana-secret-key-file;
target-file = "/run/metrics/grafana/secret.key";
user = grafana-user;
};
};
});
zones.${domain.zone} = {
grafana-postgresql-password = mkIf metricsMonitor {
source-file = grafana-database-password-file;
target-file = "/run/metrics/grafana/postgres.passwd";
user = grafana-user;
};
postgresql-grafana-password = mkIf isPostgresServer {
source-file = grafana-database-password-file;
target-file = "/run/postgres-users/grafana.passwd";
user = config.systemd.services.postgresql.serviceConfig.User;
};
};
};
zones."${domain.zone}" = {
aliases = let
metrics-aliases = alias-map-to-cnames metrics-alias-map;
monitor-aliases = alias-map-to-cnames monitor-alias-map;
@@ -217,6 +238,22 @@ in {
};
};
postgresql = mkIf isPostgresServer {
users.grafana = {
password-file = host-secrets.postgresql-grafana-password.target-file;
databases.grafana = {
access = "CONNECT";
entity-access = {
"ALL TABLES IN SCHEMA public" = "ALL PRIVILEGES";
# "SELECT,INSERT,UPDATE,DELETE";
"ALL SEQUENCES IN SCHEMA public" = "ALL PRIVILEGES";
# "SELECT, UPDATE";
};
};
};
databases.grafana.users = config.instance.local-admins;
};
metrics = {
node-exporter = {
enable = true;
@@ -234,9 +271,6 @@ in {
in "${alias}.${domain-name}";
state-directory = prometheus-cfg.state-directory;
private-network = is-private-network;
# TODO: prometheus 22.05 breaks dns_sd_configs
# Revert when fixed.
package = pkgs.pkgs2111.prometheus;
};
grafana = mkIf metricsMonitor {
@@ -252,10 +286,11 @@ in {
};
database = let cfg = grafana-cfg.database;
in {
name = cfg.name;
user = cfg.user;
password-file = cfg.password-file;
hostname = cfg.hostname;
name = "grafana";
user = "grafana";
password-file =
host-secrets.grafana-postgresql-password.target-file;
hostname = postgresServer;
};
ldap = mkIf (domain.ldap-servers != [ ]) {
hosts = map host-fqdn domain.ldap-servers;
+2
View File
@@ -2,12 +2,14 @@
{
imports = [
./service/authoritative-dns.nix
# ./service/backplane.nix
./service/chat.nix
./service/chute.nix
./service/dns.nix
./service/fudo-auth.nix
./service/jabber.nix
./service/lemmy.nix
./service/local-network.nix
./service/logging.nix
./service/mail-server.nix
+235 -202
View File
@@ -3,209 +3,242 @@
with lib;
let local-domain = "sea.fudo.org";
in {
fudo.services = {
mqtt = {
# imports = [ ./seattle/authelia.nix ./seattle/keycloak.nix ];
imports = [
# (import ./seattle/authentik.nix {
# authentikHost = "nostromo";
# proxyHost = "limina";
# externalHostname = "authentik.fudo.link";
# })
];
config = {
fudo = {
services = {
mqtt = {
enable = true;
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 = "nostromo";
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;
};
};
};
zones."sea.fudo.org" = {
aliases = {
lemmy = "nostromo";
world-of-fun = "toothless";
repland = "toothless";
};
srv-records.tcp = {
minecraft = [{
host = "toothless";
port = 25568;
}];
};
};
};
fileSystems = {
# "/mnt/documents" = {
# device = "whitedwarf.${local-domain}:/volume1/Documents";
# fsType = "nfs4";
# options = [ "comment=systemd.automount" ];
# };
# "/mnt/downloads" = {
# device = "whitedwarf.${local-domain}:/volume1/Downloads";
# fsType = "nfs4";
# options = [ "comment=systemd.automount" ];
# };
"/mnt/music" = {
device = "doraemon.${local-domain}:/volume1/Music";
fsType = "nfs";
options = [ "comment=systemd.automount" ];
};
"/mnt/video" = {
device = "doraemon.${local-domain}:/volume1/Video";
fsType = "nfs";
options = [ "comment=systemd.automount" ];
};
# fileSystems."/mnt/security" = {
# device = "panopticon.${local-domain}:/srv/kerberos/data";
# fsType = "nfs4";
# };
"/mnt/cargo_video" = {
device = "cargo.${local-domain}:/volume1/video";
fsType = "nfs4";
options = [ "sec=krb5i" "x-systemd.automount" ];
};
"/mnt/photo" = {
device = "cargo.${local-domain}:/volume1/pictures";
fsType = "nfs4";
options = [ "sec=krb5i" "x-systemd.automount" ];
};
# "proto=tcp"
# # NOTE: these are pointing directly to nostromo so the krb lookup works
"/net/documents" = {
device = "nostromo.${local-domain}:/export/documents";
fsType = "nfs4";
options = [
"sec=krb5p"
"x-systemd.automount"
# "vers=4"
# "minorversion=2"
# "proto=tcp"
];
};
"/net/downloads" = {
device = "nostromo.${local-domain}:/export/downloads";
fsType = "nfs4";
options = [
"sec=krb5i"
"x-systemd.automount"
# "vers=4"
# "minorversion=2"
# "proto=tcp"
];
};
"/net/projects" = {
device = "nostromo.${local-domain}:/export/projects";
fsType = "nfs4";
options = [
"sec=krb5p"
"x-systemd.automount"
# "vers=4"
# "minorversion=2"
# "proto=tcp"
];
};
};
systemd.services = {
host-keytab-watcher = {
wantedBy = [
"rpc-gssd-override.service"
"rpc-svcgssd-override.service"
"auth-rpcgss-module.service"
];
before = [
"rpc-gssd-override.service"
"rpc-svcgssd-override.service"
"auth-rpcgss-module.service"
];
after = [ config.fudo.secrets.secret-target ];
serviceConfig = {
ExecStartPre = "${pkgs.coreutils}/bin/test -f /etc/krb5.keytab";
ExecStart = "${pkgs.coreutils}/bin/true";
TimeoutStartSec = "360";
RemainAfterExit = true;
Restart = "on-failure";
RestartSec = "2";
};
};
auth-rpcgss-module.enable = false;
rpc-gssd.enable = false;
rpc-svcgssd.enable = false;
auth-rpcgss-module-override = {
description = "Kernel Module supporting RPCSEC_GSS";
before = [
"gssproxy.service"
"rpc-svcgssd-override.service"
"rpc-gssd-override.service"
];
wantedBy = [ "nfs-client.target" "nfs-server.target" ];
wants = [
"gssproxy.service"
"rpc-svcgssd-override.service"
"rpc-gssd-override.service"
"host-keytab-watcher.service"
];
after = [ "host-keytab-watcher.service" ];
partOf = [ "nfs-utils.service" "nfs-server.service" ];
unitConfig = {
DefaultDependencies = false;
ConditionPathExists =
[ "|!/run/gssproxy.pid" "|!/proc/net/rpc/use-gss-proxy" ];
};
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.kmod}/bin/modprobe -q auth_rpcgss";
RemainAfterExit = true;
};
};
rpc-gssd-override = {
description = "RPC security service for NFS client and server";
wantedBy = [ "auth-rpcgss-module.service" ];
conflicts = [ "umount.target" ];
after = [
"host-keytab-watcher.service"
"rpc_pipefs.target"
"local-fs.target"
];
wants = [ "host-keytab-watcher.service" ];
requires = [ "rpc_pipefs.target" ];
partOf = [ "nfs-utils.service" ];
unitConfig.DefaultDependencies = false;
serviceConfig = {
Type = "forking";
ExecStart = "${pkgs.nfs-utils}/bin/rpc.gssd";
Restart = "always";
};
};
rpc-svcgssd-override = {
description = "RPC security service for NFS server";
wantedBy = [ "auth-rpcgss-module.service" ];
after = [
"host-keytab-watcher.service"
"local-fs.target"
"gssproxy.service"
];
wants = [ "host-keytab-watcher.service" ];
partOf = [ "nfs-utils.service" "nfs-server.service" ];
unitConfig = {
DefaultDependencies = false;
ConditionPathExists =
[ "|!/run/gssproxy.pid" "|!/proc/net/rpc/use-gss-proxy" ];
};
serviceConfig = {
Type = "forking";
ExecStart = "${pkgs.nfs-utils}/bin/rpc.svcgssd";
Restart = "always";
};
};
rpcbind.after = [ "local-fs.target" ];
};
services.printing = {
enable = true;
host = "wormhole0";
drivers = [ pkgs.brgenml1cupswrapper ];
};
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 = {
# "/mnt/documents" = {
# device = "whitedwarf.${local-domain}:/volume1/Documents";
# fsType = "nfs4";
# options = [ "comment=systemd.automount" ];
# };
# "/mnt/downloads" = {
# device = "whitedwarf.${local-domain}:/volume1/Downloads";
# fsType = "nfs4";
# options = [ "comment=systemd.automount" ];
# };
"/mnt/music" = {
device = "doraemon.${local-domain}:/volume1/Music";
fsType = "nfs";
options = [ "comment=systemd.automount" ];
};
"/mnt/video" = {
device = "doraemon.${local-domain}:/volume1/Video";
fsType = "nfs";
options = [ "comment=systemd.automount" ];
};
# fileSystems."/mnt/security" = {
# device = "panopticon.${local-domain}:/srv/kerberos/data";
# fsType = "nfs4";
# };
"/mnt/cargo_video" = {
device = "cargo.${local-domain}:/volume1/video";
fsType = "nfs4";
options = [ "sec=krb5i" "x-systemd.automount" ];
};
"/mnt/photo" = {
device = "cargo.${local-domain}:/volume1/pictures";
fsType = "nfs4";
options = [ "sec=krb5i" "x-systemd.automount" ];
};
# "proto=tcp"
# # NOTE: these are pointing directly to nostromo so the krb lookup works
"/net/documents" = {
device = "nostromo.${local-domain}:/export/documents";
fsType = "nfs4";
options = [
"sec=krb5p"
"x-systemd.automount"
# "vers=4"
# "minorversion=2"
# "proto=tcp"
];
};
"/net/downloads" = {
device = "nostromo.${local-domain}:/export/downloads";
fsType = "nfs4";
options = [
"sec=krb5i"
"x-systemd.automount"
# "vers=4"
# "minorversion=2"
# "proto=tcp"
];
};
"/net/projects" = {
device = "nostromo.${local-domain}:/export/projects";
fsType = "nfs4";
options = [
"sec=krb5p"
"x-systemd.automount"
# "vers=4"
# "minorversion=2"
# "proto=tcp"
];
};
};
systemd.services = {
host-keytab-watcher = {
wantedBy = [
"rpc-gssd-override.service"
"rpc-svcgssd-override.service"
"auth-rpcgss-module.service"
];
before = [
"rpc-gssd-override.service"
"rpc-svcgssd-override.service"
"auth-rpcgss-module.service"
];
after = [ config.fudo.secrets.secret-target ];
serviceConfig = {
ExecStartPre = "${pkgs.coreutils}/bin/test -f /etc/krb5.keytab";
ExecStart = "${pkgs.coreutils}/bin/true";
TimeoutStartSec = "360";
RemainAfterExit = true;
Restart = "on-failure";
RestartSec = "2";
};
};
auth-rpcgss-module.enable = false;
rpc-gssd.enable = false;
rpc-svcgssd.enable = false;
auth-rpcgss-module-override = {
description = "Kernel Module supporting RPCSEC_GSS";
before = [
"gssproxy.service"
"rpc-svcgssd-override.service"
"rpc-gssd-override.service"
];
wantedBy = [ "nfs-client.target" "nfs-server.target" ];
wants = [
"gssproxy.service"
"rpc-svcgssd-override.service"
"rpc-gssd-override.service"
"host-keytab-watcher.service"
];
after = [ "host-keytab-watcher.service" ];
partOf = [ "nfs-utils.service" "nfs-server.service" ];
unitConfig = {
DefaultDependencies = false;
ConditionPathExists =
[ "|!/run/gssproxy.pid" "|!/proc/net/rpc/use-gss-proxy" ];
};
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.kmod}/bin/modprobe -q auth_rpcgss";
RemainAfterExit = true;
};
};
rpc-gssd-override = {
description = "RPC security service for NFS client and server";
wantedBy = [ "auth-rpcgss-module.service" ];
conflicts = [ "umount.target" ];
after =
[ "host-keytab-watcher.service" "rpc_pipefs.target" "local-fs.target" ];
wants = [ "host-keytab-watcher.service" ];
requires = [ "rpc_pipefs.target" ];
partOf = [ "nfs-utils.service" ];
unitConfig.DefaultDependencies = false;
serviceConfig = {
Type = "forking";
ExecStart = "${pkgs.nfs-utils}/bin/rpc.gssd";
Restart = "always";
};
};
rpc-svcgssd-override = {
description = "RPC security service for NFS server";
wantedBy = [ "auth-rpcgss-module.service" ];
after =
[ "host-keytab-watcher.service" "local-fs.target" "gssproxy.service" ];
wants = [ "host-keytab-watcher.service" ];
partOf = [ "nfs-utils.service" "nfs-server.service" ];
unitConfig = {
DefaultDependencies = false;
ConditionPathExists =
[ "|!/run/gssproxy.pid" "|!/proc/net/rpc/use-gss-proxy" ];
};
serviceConfig = {
Type = "forking";
ExecStart = "${pkgs.nfs-utils}/bin/rpc.svcgssd";
Restart = "always";
};
};
rpcbind.after = [ "local-fs.target" ];
};
services.printing = {
enable = true;
drivers = [ pkgs.brgenml1cupswrapper ];
};
}
+71
View File
@@ -0,0 +1,71 @@
{ config, lib, pkgs, ... }:
with lib;
let
hostname = config.instance.hostname;
host = config.fudo.hosts."${hostname}";
domainName = host.domain;
domain = config.fudo.domains."${domainName}";
zone = domain.zone;
autheliaHostname = "authelia.fudo.link";
autheliaHost = "nostromo";
gatewayHost = "limina";
autheliaFqdn = pkgs.lib.getHostFqdn autheliaHost;
autheliaPort = 7065;
isAuthelia = hostname == autheliaHost;
isProxy = hostname == gatewayHost;
hostSecrets = config.fudo.secrets.host-secrets."${hostname}";
in {
config = {
fudo.zones."${zone}".aliases.authelia = autheliaHost;
services = {
authelia.instances.seattle = mkIf isAuthelia {
enable = true;
settings = {
server.port = autheliaPort;
default_2fa_method = "webauthn";
};
secrets = {
jwtSecretFile =
config.fudo.secrets.files.service-secrets."${hostname}"."authelia.jwt";
storageEncryptionKeyFile =
config.fudo.secrets.files.service-secrets."${hostname}"."authelia.storage";
};
};
nginx = mkIf (isProxy || isAuthelia) {
enable = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
virtualHosts = {
# "${keycloakHostname}" = mkIf isProxy {
# enableACME = true;
# forceSSL = true;
# locations."/" = {
# proxyPass = "http://keycloak.${domainName}:80";
# proxyWebsockets = true;
# };
# };
"authelia.${domainName}" = mkIf isAuthelia {
enableACME = false;
forceSSL = false;
locations."/" = {
proxyPass = "http://localhost:${toString autheliaPort}";
proxyWebsockets = true;
};
};
};
};
};
};
}
+56
View File
@@ -0,0 +1,56 @@
{ authentikHost, proxyHost, externalHostname, ... }:
{ config, lib, pkgs, ... }:
with lib;
let
hostname = config.instance.hostname;
authentikFqdn = pkgs.lib.getHostFqdn authentikHost;
isAuthentik = hostname == authentikHost;
isProxy = hostname == proxyHost;
host = config.fudo.hosts."${hostname}";
domainName = host.domain;
zoneName = config.fudo.domains."${domainName}".zone;
in {
config = {
fudo.zones."${zoneName}".aliases.authentik = authentikHost;
services = {
authentikContainer = mkIf (authentikHost == hostname) {
enable = isAuthentik;
images = {
authentik = "ghcr.io/goauthentik/server:2023.6.1";
postgres = "docker.io/library/postgres:12-alpine";
redis = "docker.io/library/redis:alpine";
};
};
nginx = mkIf (isProxy || isAuthentik) {
enable = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
virtualHosts = {
"authentik.${domainName}" = mkIf isAuthentik {
enableACME = false;
forceSSL = false;
locations."/" = {
proxyPass = "http://localhost:${
toString config.services.authentikContainer.ports.http
}";
proxyWebsockets = true;
};
};
"${externalHostname}" = mkIf isProxy {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://authentik.${domainName}:80";
proxyWebsockets = true;
};
};
};
};
};
};
}
@@ -0,0 +1,8 @@
{ homeAsssistantImage, nodeRedImage, ... }:
{ config, lib, pkgs, ... }:
with lib;
{
}
+112
View File
@@ -0,0 +1,112 @@
{ config, lib, pkgs, ... }:
with lib;
let
hostname = config.instance.hostname;
host = config.fudo.hosts."${hostname}";
domainName = host.domain;
domain = config.fudo.domains."${domainName}";
zone = domain.zone;
keycloakHostname = "keycloak.fudo.link";
keycloakHost = "nostromo";
gatewayHost = "limina";
postgresHost = domain.postgresql-server;
keycloakFqdn = pkgs.lib.getHostFqdn keycloakHost;
postgresFqdn = pkgs.lib.getHostFqdn postgresHost;
keycloakPort = 9085;
isKeycloak = hostname == keycloakHost;
isProxy = hostname == gatewayHost;
isPostgres = hostname == postgresHost;
hostSecrets = config.fudo.secrets.host-secrets."${hostname}";
postgresPasswd =
pkgs.lib.passwd.stablerandom-passwd-file "keycloak-postgres-passwd"
config.instance.build-seed;
in {
config = {
fudo = {
secrets.host-secrets."${hostname}" = {
keycloak-postgres-passwd = mkIf isKeycloak {
source-file = postgresPasswd;
target-file = "/run/keycloak/postgres.passwd";
};
postgres-keycloak-passwd = mkIf isPostgres {
source-file = postgresPasswd;
target-file = "/run/postgres/keycloak.passwd";
user = config.systemd.services.postgresql.serviceConfig.User;
};
};
zones."${zone}".aliases.keycloak = keycloakHost;
postgresql = mkIf isPostgres {
required-services = [ "fudo-passwords.target" ];
databases.keycloak.users = config.instance.local-admins;
users.keycloak = {
password-file = hostSecrets.postgres-keycloak-passwd.target-file;
databases.keycloak = {
access = "ALL PRIVILEGES";
entity-access = {
"ALL TABLES IN SCHEMA public" = "SELECT,INSERT,UPDATE,DELETE";
"ALL SEQUENCES IN SCHEMA public" = "SELECT,UPDATE";
};
};
};
};
};
services = {
keycloak = mkIf isKeycloak {
enable = true;
settings = {
http-port = keycloakPort;
hostname = keycloakHostname;
proxy = "edge";
};
database = {
host = postgresFqdn;
type = "postgresql";
name = "keycloak";
username = "keycloak";
passwordFile = hostSecrets.keycloak-postgres-passwd.target-file;
useSSL = false;
};
};
nginx = mkIf (isProxy || isKeycloak) {
enable = true;
recommendedOptimisation = true;
recommendedProxySettings = true;
virtualHosts = {
# "${keycloakHostname}" = mkIf isProxy {
# enableACME = true;
# forceSSL = true;
# locations."/" = {
# proxyPass = "http://keycloak.${domainName}:80";
# proxyWebsockets = true;
# };
# };
"keycloak.${domainName}" = mkIf isKeycloak {
enableACME = false;
forceSSL = false;
locations."/" = {
proxyPass = "http://localhost:${toString keycloakPort}";
proxyWebsockets = true;
};
};
};
};
};
};
}
+5
View File
@@ -0,0 +1,5 @@
{ config, lib, pkgs, ... }:
{
}
+643 -514
View File
File diff suppressed because it is too large Load Diff
+1 -3
View File
@@ -1,7 +1,5 @@
{ config, lib, pkgs, ... }:
{
imports = [
./zones/selby.ca.nix
];
imports = [ ./zones/sea.fudo.org.nix ./zones/selby.ca.nix ];
}
+3
View File
@@ -0,0 +1,3 @@
{ config, lib, pkgs, ... }:
{ }