Tons of stuff, including 23.05 -> 23.11

This commit is contained in:
niten 2024-01-16 19:08:38 -08:00
parent 0027566304
commit 7a73028ad2
28 changed files with 837 additions and 1163 deletions

View File

@ -4,7 +4,8 @@ with lib;
let let
hostname = config.instance.hostname; hostname = config.instance.hostname;
localDomain = "fudo.org"; localDomain = "fudo.org";
serviceSecrets = config.fudo.secrets.files.service-secrets."${hostname}";
domainSecrets = config.fudo.secrets.files.domain-secrets."${localDomain}";
inherit (pkgs.lib) getDomainHosts getHostIpv4 getHostIpv6 getHostFqdn; inherit (pkgs.lib) getDomainHosts getHostIpv4 getHostIpv6 getHostFqdn;
@ -16,7 +17,10 @@ let
defaultHost = "germany"; defaultHost = "germany";
mastodonHostname = "mastodon.fudo.org"; mastodonHostname = "fudo.live";
lemmyHost = "germany";
lemmyHostname = "fudo.social";
servedDomains = [ servedDomains = [
"fudo.org" "fudo.org"
@ -24,86 +28,117 @@ let
"selby.ca" "selby.ca"
"fudo.ca" "fudo.ca"
"fudo.im" "fudo.im"
"fudo.live"
"fudo.social"
"stewartsoundservices.ca" "stewartsoundservices.ca"
]; ];
in { in {
imports = [ imports = [
(import ./fudo.org/authentik.nix { inherit authentikHost; }) (import ./fudo.org/authentik.nix {
inherit authentikHost;
authentikImage = "ghcr.io/goauthentik/server:2023.10.6";
})
(import ./fudo.org/mastodon.nix { (import ./fudo.org/mastodon.nix {
mastodonHost = "legatus"; mastodonHost = "germany";
mastodonHostname = mastodonHostname; mastodonHostname = mastodonHostname;
mastodonWebDomain = "fudo.org"; mastodonWebDomain = mastodonHostname;
mastodonOidcClientId = serviceSecrets."mastodon-oidc.clientid"; mastodonOidcClientId = domainSecrets."mastodon-oidc.clientid";
mastodonOidcClientSecret = serviceSecrets."mastodon-oidc.secret"; mastodonOidcClientSecret = domainSecrets."mastodon-oidc.secret";
}) })
(import ./fudo.org/nextcloud.nix { (import ./fudo.org/nextcloud.nix {
nextcloudHost = "legatus"; nextcloudHost = "legatus";
nextcloudHostname = "cloud.fudo.org"; nextcloudHostname = "cloud.fudo.org";
nextcloudPackage = pkgs.nextcloud27; nextcloudPackage = pkgs.nextcloud28;
}) })
(import ./fudo.org/matrix.nix { (import ./fudo.org/matrix.nix {
matrixHost = "legatus"; matrixHost = "germany";
matrixServerName = "fudo.org"; matrixServerName = "fudo.im";
openIdClientId = readFile serviceSecrets."matrix-oidc.clientid"; openIdClientId = readFile domainSecrets."matrix-oidc.clientid";
openIdClientSecret = readFile serviceSecrets."matrix-oidc.secret"; openIdClientSecret = readFile domainSecrets."matrix-oidc.secret";
}) })
(import ./fudo.org/mail-server.nix (rec { (import ./fudo.org/mail-server.nix (rec {
primaryMailserver = "germany"; primaryMailserver = "france";
primaryDomain = "test.fudo.org"; primaryDomain = "fudo.org";
authentikServer = "authentik.fudo.org"; authentikServer = "authentik.fudo.org";
ldapBase = "dc=fudo,dc=org"; ldapBase = "dc=fudo,dc=org";
ldapBindDn = "cn=userdb,ou=users,${ldapBase}"; ldapBindDn = "cn=userdb,ou=users,${ldapBase}";
ldapBindPwFile = ldapBindPwFile = domainSecrets."ldap-bind.passwd";
config.fudo.secrets.files.domain-secrets."${primaryDomain}"."ldap-bind.passwd";
saslDomain = "FUDO.ORG"; saslDomain = "FUDO.ORG";
authentikOutpostToken = authentikOutpostToken = domainSecrets."authentik-ldap.token";
config.fudo.secrets.files.domain-secrets."${primaryDomain}"."authentik-ldap.token";
inherit servedDomains; inherit servedDomains;
# TODO: FIXME! dkimRecord = ''
dkimRecord = ""; mail._domainkey IN TXT ( "v=DKIM1;k=rsa;p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwoCgHwsfuu0lhj9Ayj4ntoy0bdcGBNsV46qoKbd8E8FKsJF5rL4EoytwXEFcKJwT3E+o3/BsZGi9J5irtjlfIhnxnPlhVRS3R/834NDSQyuwGTxAfhPOklhA0cTYA+4x2oGwAuraz+On2REDeSymMccXFDsTugEHVvn6qaeqkJwIDAQAB" );'';
})) }))
]; ];
config = { config = {
# All Fudo hosts should redirect selby.ca to the selbyhomecentre website. # All Fudo hosts should redirect selby.ca to the selbyhomecentre website.
services.nginx.virtualHosts = { services = {
# Pass requests to selby on to selbyhomecentre nginx.virtualHosts = {
"selby.ca".locations."/".return = # Pass requests to selby on to selbyhomecentre
"301 https://selbyhomecentre.com$request_uri"; "selby.ca".locations."/".return =
"www.selby.ca".locations."/".return = "301 https://selbyhomecentre.com$request_uri";
"301 https://selbyhomecentre.com$request_uri"; "www.selby.ca".locations."/".return =
"301 https://selbyhomecentre.com$request_uri";
# For Mastodon # For Mastodon
"fudo.org".locations = { "fudo.org".locations = {
"/.well-known/webfinger" = { "/.well-known/webfinger" = {
return = "301 http://${mastodonHostname}"; return = "301 http://${mastodonHostname}";
extraConfig = "add_header Access-Control-Allow-Origin '*';"; extraConfig = "add_header Access-Control-Allow-Origin '*';";
};
"/.well-known/host-meta" = {
return = "301 https://${mastodonHostname}$request_uri";
};
}; };
"/.well-known/host-meta" = { };
return = "301 https://${mastodonHostname}$request_uri";
lemmyDocker = {
enable = config.instance.hostname == lemmyHost;
hostname = lemmyHostname;
site-name = "Fudo Lemmy";
version = "0.19.2";
smtp-server = "mail.fudo.org:587";
docker-images = {
pictrs = "asonix/pictrs:0.5.1";
postgres = "postgres:15-alpine";
}; };
}; };
}; };
fudo = { fudo = {
services = { services = {
# TEMPORARY
mail-server.enable = false;
jabber = { jabber = {
domain = "jabber.fudo.org"; domain = "jabber.fudo.org";
ldap.servers = ldap.servers =
map (host: "${host}.${localDomain}") domain.ldap-servers; map (host: "${host}.${localDomain}") domain.ldap-servers;
}; };
metrics.grafana = {
oauth = {
hostname = "authentik.fudo.org";
client-id = domainSecrets."grafana-oid-client-id";
client-secret = domainSecrets."grafana-oid-client-secret";
slug = "grafana-metrics";
};
};
authoritative-dns = { authoritative-dns = {
enable = hostname == primaryNameserver; enable = hostname == primaryNameserver;
enable-notifications = true;
container = { container = {
hostname = "nameserver"; hostname = "nameserver";
interface = "enp5s0f0"; interface = "enp5s0f0";
}; };
nameservers = { nameservers = {
primary = primaryNameserver; primary = "nameserver";
external = map (hostname: { external = map (hostname: {
inherit (config.fudo.zones."fudo.org".hosts."${hostname}") inherit (config.fudo.zones."fudo.org".hosts."${hostname}")
ipv4-address ipv6-address description; ipv4-address ipv6-address description;
@ -124,9 +159,10 @@ in {
description = "fudo.org"; description = "fudo.org";
}; };
# TODO: Fix email FFS!
fudoMailservers = { fudoMailservers = {
smtp-servers = [ "smtp.fudo.org." ]; smtp-servers = [ "mail.fudo.org." ];
imap-servers = [ "imap.fudo.org." ]; imap-servers = [ "mail.fudo.org." ];
}; };
mkDomain = domain: extraConfig: mkDomain = domain: extraConfig:
@ -147,6 +183,7 @@ in {
"stewartsoundservices.ca" = "stewartsoundservices.ca" =
mkDomain "stewartsoundservices.ca" { mail = fudoMailservers; }; mkDomain "stewartsoundservices.ca" { mail = fudoMailservers; };
"fudo.live" = mkDomain "fudo.live" { mail = fudoMailservers; }; "fudo.live" = mkDomain "fudo.live" { mail = fudoMailservers; };
"fudo.social" = mkDomain "fudo.social" { mail = fudoMailservers; };
}; };
}; };
}; };

View File

@ -1,4 +1,4 @@
{ authentikHost, ... }: { authentikHost, authentikImage, ... }:
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
@ -24,7 +24,7 @@ in {
authentikContainer = mkIf isAuthentik { authentikContainer = mkIf isAuthentik {
enable = true; enable = true;
images = { images = {
authentik = "ghcr.io/goauthentik/server:2023.8.3"; authentik = authentikImage;
postgres = "docker.io/library/postgres:12-alpine"; postgres = "docker.io/library/postgres:12-alpine";
redis = "docker.io/library/redis:alpine"; redis = "docker.io/library/redis:alpine";
}; };
@ -50,32 +50,8 @@ in {
proxyWebsockets = true; 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}
'';
}));
}; };
} }

View File

@ -16,19 +16,17 @@ in {
after = [ "podman.service" ]; after = [ "podman.service" ];
}; };
fudo = { # security.acme.certs = {
acme.host-domains = { # "imap.${primaryDomain}".extraDomainNames = [ "mail.${primaryDomain}" ];
"imap.${primaryDomain}".extra-domain = [ "mail.${primaryDomain}" ]; # "smtp.${primaryDomain}".extraDomainNames = [ "mail.${primaryDomain}" ];
"smtp.${primaryDomain}".extra-domain = [ "mail.${primaryDomain}" ]; # };
};
fudo = {
zones."${primaryDomain}" = let zones."${primaryDomain}" = let
mailserverDomain = config.fudo.hosts."${primaryMailserver}".domain; mailserverDomain = config.fudo.hosts."${primaryMailserver}".domain;
mailserver =
config.fudo.domains."${mailserverDomain}".primary-mailserver;
mailserverIps = { mailserverIps = {
ipv4-address = getHostIpv4 mailserver; ipv4-address = getHostIpv4 primaryMailserver;
ipv6-address = getHostIpv6 mailserver; ipv6-address = getHostIpv6 primaryMailserver;
}; };
srvRecord = host: port: [{ inherit host port; }]; srvRecord = host: port: [{ inherit host port; }];
in { in {
@ -66,6 +64,7 @@ in {
enable = hostname == primaryMailserver; enable = hostname == primaryMailserver;
debug = true; debug = true;
primary-domain = primaryDomain; primary-domain = primaryDomain;
extra-domains = servedDomains;
sasl-domain = saslDomain; sasl-domain = saslDomain;
trusted-networks = config.instance.local-networks; trusted-networks = config.instance.local-networks;
smtp = { smtp = {
@ -110,12 +109,17 @@ in {
services.nginx = mkIf (hostname == primaryMailserver) { services.nginx = mkIf (hostname == primaryMailserver) {
enable = true; enable = true;
virtualHosts = { virtualHosts = {
"smtp.${primaryDomain}" = { "imap.${primaryDomain}" = {
enableACME = true; enableACME = true;
forceSSL = true; forceSSL = true;
locations."/".return = "301 https://webmail.${primaryDomain}"; locations."/".return = "301 https://webmail.${primaryDomain}";
}; };
"imap.${primaryDomain}" = { "mail.${primaryDomain}" = {
enableACME = true;
forceSSL = true;
locations."/".return = "301 https://webmail.${primaryDomain}";
};
"smtp.${primaryDomain}" = {
enableACME = true; enableACME = true;
forceSSL = true; forceSSL = true;
locations."/".return = "301 https://webmail.${primaryDomain}"; locations."/".return = "301 https://webmail.${primaryDomain}";

View File

@ -5,17 +5,19 @@
with lib; with lib;
let let
hostname = config.instance.hostname; hostname = config.instance.hostname;
domainName = "fudo.org"; domainName = "fudo.im";
zoneName = config.fudo.domains."${domainName}".zone; zoneName = config.fudo.domains."${domainName}".zone;
isMatrix = hostname == matrixHost; isMatrix = hostname == matrixHost;
matrixFqdn = "matrix.${domainName}"; matrixFqdn = "matrix.${domainName}";
in { in {
config = { config = {
fudo = { fudo = {
zones."${zoneName}".aliases = { zones."${zoneName}".aliases =
element = matrixHost; let matrixHostFqdn = pkgs.lib.getHostFqdn matrixHost;
matrix = matrixHost; in {
}; web = "${matrixHostFqdn}.";
matrix = "${matrixHostFqdn}.";
};
services.matrix = mkIf isMatrix { services.matrix = mkIf isMatrix {
enable = true; enable = true;
@ -31,7 +33,7 @@ in {
}; };
}; };
networking.firewall.allowedTCPPorts = [ 8008 8448 ]; networking.firewall.allowedTCPPorts = [ 80 443 8008 8448 ];
services.nginx.virtualHosts = mkIf isMatrix { services.nginx.virtualHosts = mkIf isMatrix {
"${domainName}" = let "${domainName}" = let
@ -42,6 +44,7 @@ in {
''; '';
in { in {
enableACME = true; enableACME = true;
forceSSL = true;
listen = [ listen = [
{ {
addr = "0.0.0.0"; addr = "0.0.0.0";
@ -64,15 +67,20 @@ in {
ssl = true; ssl = true;
} }
]; ];
locations."/.well-known/matrix/server".extraConfig = locations = {
mkWellKnown { "m.server" = "${matrixFqdn}:443"; }; "/.well-known/matrix/server".extraConfig =
locations."/.well-known/matrix/client".extraConfig = mkWellKnown { "m.server" = "${matrixFqdn}:443"; };
mkWellKnown { "m.homeserver".base_url = "https://${matrixFqdn}"; }; "/.well-known/matrix/client".extraConfig = mkWellKnown {
"m.homeserver".base_url = "https://${matrixFqdn}:443";
};
"/.well-known/acme-challenge" = {
root = "/var/lib/acme/acme-challenge/";
extraConfig = "auth_basic off;";
};
"/".return = "301 https://web.${domainName}";
};
}; };
# "${matrixFqdn}" = { "web.${domainName}" = {
# locations."^/$".return = "301 https://element.${domainName}";
# };
"element.${domainName}" = {
enableACME = true; enableACME = true;
forceSSL = true; forceSSL = true;
root = pkgs.element-web.override { root = pkgs.element-web.override {
@ -82,7 +90,7 @@ in {
"https://${matrixFqdn}"; "https://${matrixFqdn}";
brand = "Fudo"; brand = "Fudo";
room_directory.servers = room_directory.servers =
[ "fudo.org" "matrix.org" "libera.chat" "gitter.im" ]; [ matrixFqdn "matrix.org" "libera.chat" "gitter.im" ];
map_style_url = map_style_url =
"https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx"; "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx";
}; };

View File

@ -20,8 +20,8 @@ in {
package = nextcloudPackage; package = nextcloudPackage;
extra-apps = extra-apps =
with config.services.nextcloudContainer.package.packages.apps; { with config.services.nextcloudContainer.package.packages.apps; {
inherit news contacts calendar tasks maps memories mail bookmarks inherit contacts calendar tasks maps mail bookmarks notes user_saml;
files_markdown notes unsplash user_saml; # files_markdown memories news unsplash
}; };
timezone = "America/Winnipeg"; timezone = "America/Winnipeg";
}; };

View File

@ -14,11 +14,25 @@ let
in { in {
config = { config = {
fudo.zones."sea.fudo.org".aliases."frigate" = "zbox"; fudo.zones."sea.fudo.org".aliases = { "frigate" = "zbox"; };
fudo = { fudo = {
services.mqtt.private.users.frigate = { services = {
password-file = frigateMqttPassword; metrics = {
acl = [ "frigate/#" ]; private-network = true;
grafana.oauth = {
hostname = "authentik.fudo.org";
client-id =
config.fudo.secrets.files.domain-secrets."fudo.org"."grafana-oid-client-id";
client-secret =
config.fudo.secrets.files.domain-secrets."fudo.org"."grafana-oid-client-secret";
slug = "grafana-metrics";
};
};
mqtt.private.users.frigate = {
password-file = frigateMqttPassword;
acl = [ "frigate/#" ];
};
}; };
}; };

View File

@ -73,6 +73,18 @@ in {
fsType = "btrfs"; fsType = "btrfs";
options = [ "subvol=@acme" "compress=zstd" "noatime" "noexec" ]; options = [ "subvol=@acme" "compress=zstd" "noatime" "noexec" ];
}; };
"/state/services/mail/mail" = {
device = "/dev/disk/by-label/germany-data";
fsType = "btrfs";
options = [ "subvol=@mail" "compress=zstd" "noatime" "noexec" ];
};
"/var/lib/containers/storage" = {
device = "/dev/disk/by-label/germany-data";
fsType = "btrfs";
options = [ "subvol=@container-data" "noatime" "compress=zstd" "noexec" ];
};
}; };
swapDevices = [{ device = "/dev/disk/by-label/germany-swap"; }]; swapDevices = [{ device = "/dev/disk/by-label/germany-swap"; }];
@ -87,30 +99,4 @@ in {
cpu.intel.updateMicrocode = true; cpu.intel.updateMicrocode = true;
enableAllFirmware = true; enableAllFirmware = true;
}; };
networking = {
useDHCP = false;
macvlans = {
extif0 = {
interface = "enp5s0f0";
mode = "bridge";
};
dnsif0 = {
interface = "enp5s0f0";
mode = "bridge";
};
extif1 = {
interface = "enp5s0f1";
mode = "bridge";
};
};
interfaces = {
extif0.macAddress = generateMac config.instance.hostname "extif0";
extif1.macAddress = generateMac config.instance.hostname "extif1";
};
};
} }

View File

@ -93,6 +93,8 @@ in {
hosts.fimbria.external-interfaces = [ "enp1s0" ]; hosts.fimbria.external-interfaces = [ "enp1s0" ];
client.dns.external-interface = "enp1s0"; client.dns.external-interface = "enp1s0";
local-network.state-directory = "/state/services/local-network";
secrets.host-secrets."${hostname}" = { secrets.host-secrets."${hostname}" = {
sea-cam-auth-proxy-env = { sea-cam-auth-proxy-env = {
source-file = let source-file = let
@ -135,7 +137,16 @@ in {
security.acme.defaults.email = "niten@fudo.org"; security.acme.defaults.email = "niten@fudo.org";
systemd.services.nginx.requires = [ "bind.service" ]; systemd.services = {
nginx = {
after = [ "bind.service" ];
requires = [ "bind.service" ];
};
podman-sea-cam-auth-proxy.after = [ "network-online.service" ];
podman-sea-red-auth-proxy.after = [ "network-online.service" ];
};
services = { services = {
## TODO: enable when ready ## TODO: enable when ready
@ -223,6 +234,20 @@ in {
target = "http://node-red.sea.fudo.org/"; target = "http://node-red.sea.fudo.org/";
authPort = 9001; authPort = 9001;
}; };
"metrics.fudo.link" = {
enableACME = true;
forceSSL = true;
locations."/" = {
proxyPass = "http://prometheus.sea.fudo.org";
extraConfig = let
trustedNetworkClauses = map (nw: "allow ${nw};")
(config.instance.local-networks
++ config.fudo.domains."fudo.org".local-networks);
in concatStringsSep "\n"
(trustedNetworkClauses ++ [ "deny all;" ]);
};
};
}; };
}; };

View File

@ -14,14 +14,15 @@ let
secrets = config.fudo.secrets.host-secrets.france; secrets = config.fudo.secrets.host-secrets.france;
secret-files = config.fudo.secrets.files; secret-files = config.fudo.secrets.files;
letsencrypt-full-chain = name: chain: pkgs.stdenv.mkDerivation { letsencrypt-full-chain = name: chain:
name = "${name}-letsencrypt-full-chain.pem"; pkgs.stdenv.mkDerivation {
phases = "installPhase"; name = "${name}-letsencrypt-full-chain.pem";
installPhase = '' phases = "installPhase";
cat ${chain} > $out installPhase = ''
cat ${pkgs.letsencrypt-ca}/ca.pem >> $out cat ${chain} > $out
''; cat ${pkgs.letsencrypt-ca}/ca.pem >> $out
}; '';
};
in { in {
# imports = let # imports = let
@ -88,21 +89,17 @@ in {
}; };
fudo = let fudo = let
backplane-dns-password-file = pkgs.lib.passwd.stablerandom-passwd-file backplane-dns-password-file =
"dns-service-backplane-passwd" pkgs.lib.passwd.stablerandom-passwd-file "dns-service-backplane-passwd"
"dns-service-backplane-passwd-${config.instance.build-seed}"; "dns-service-backplane-passwd-${config.instance.build-seed}";
in { in {
hosts.france.external-interfaces = [ "extif0" ]; hosts.france.external-interfaces = [ "extif0" ];
acme.host-domains.france."france.fudo.org" = { acme.host-domains.france."france.fudo.org" = {
email = "admin@fudo.org"; admin-email = "admin@fudo.org";
local-copies = { local-copies = {
postgres = { postgres = { user = "postgres"; };
user = "postgres"; openldap = { user = config.services.openldap.user; };
};
openldap = {
user = config.services.openldap.user;
};
}; };
}; };
@ -142,93 +139,93 @@ in {
external-interface = "extif0"; external-interface = "extif0";
}; };
# france = { # france = {
# ldap = let # ldap = let
# cert-copy = config.fudo.acme.host-domains.france."france.fudo.org".local-copies.openldap; # cert-copy = config.fudo.acme.host-domains.france."france.fudo.org".local-copies.openldap;
# chain = "${letsencrypt-full-chain "openldap-france" cert-copy.chain}"; # chain = "${letsencrypt-full-chain "openldap-france" cert-copy.chain}";
# in { # in {
# ssl-certificate = cert-copy.certificate; # ssl-certificate = cert-copy.certificate;
# ssl-private-key = cert-copy.private-key; # ssl-private-key = cert-copy.private-key;
# ssl-ca-certificate = chain; # ssl-ca-certificate = chain;
# keytab = secrets.ldap-keytab.target-file; # keytab = secrets.ldap-keytab.target-file;
# root-password-file = secrets.ldap-root-passwd.target-file; # root-password-file = secrets.ldap-root-passwd.target-file;
# }; # };
# kdc = { # kdc = {
# state-directory = "/state/kerberos"; # state-directory = "/state/kerberos";
# master-key-file = secret-files.realm-master-keys."FUDO.ORG"; # master-key-file = secret-files.realm-master-keys."FUDO.ORG";
# listen-ips = [ primary-ip "127.0.0.1" "127.0.1.1" "::1" ]; # listen-ips = [ primary-ip "127.0.0.1" "127.0.1.1" "::1" ];
# }; # };
# jabber = { # jabber = {
# ldap-servers = [ "france.fudo.org" ]; # ldap-servers = [ "france.fudo.org" ];
# listen-ips = [ primary-ip ]; # listen-ips = [ primary-ip ];
# backplane = { # backplane = {
# host-passwd-files = let # host-passwd-files = let
# hosts = attrNames config.fudo.hosts; # hosts = attrNames config.fudo.hosts;
# in mapAttrs (hostname: hostOpts: hostOpts.backplane-password-file) # in mapAttrs (hostname: hostOpts: hostOpts.backplane-password-file)
# config.fudo.hosts; # config.fudo.hosts;
# service-passwd-files = { # service-passwd-files = {
# dns = backplane-dns-password-file; # dns = backplane-dns-password-file;
# }; # };
# }; # };
# }; # };
# backplane-server = { # backplane-server = {
# listen-ips = [ primary-ip ]; # listen-ips = [ primary-ip ];
# backplane-dns-password-file = # backplane-dns-password-file =
# secrets.backplane-dns-password.target-file; # secrets.backplane-dns-password.target-file;
# }; # };
# mail = { # mail = {
# mail-directory = "${mail-directory}/mailboxes"; # mail-directory = "${mail-directory}/mailboxes";
# state-directory = "${mail-directory}/var"; # state-directory = "${mail-directory}/var";
# ldap-server-urls = [ # ldap-server-urls = [
# "ldap://france.fudo.org" # "ldap://france.fudo.org"
# ]; # ];
# }; # };
# webmail = { # webmail = {
# mail-server = mail-hostname; # mail-server = mail-hostname;
# database.hostname = "localhost"; # database.hostname = "localhost";
# }; # };
# git = { # git = {
# repository-directory = "/state/gitea/repo"; # repository-directory = "/state/gitea/repo";
# state-directory = "/state/gitea/state"; # state-directory = "/state/gitea/state";
# ssh.listen-ip = git-server-ip; # ssh.listen-ip = git-server-ip;
# database-host = "localhost"; # database-host = "localhost";
# }; # };
# postgresql = let # postgresql = let
# cert-copy = config.fudo.acme.host-domains.france."france.fudo.org".local-copies.postgres; # cert-copy = config.fudo.acme.host-domains.france."france.fudo.org".local-copies.postgres;
# in { # in {
# keytab = secrets.postgres-keytab.target-file; # keytab = secrets.postgres-keytab.target-file;
# ssl-certificate = cert-copy.certificate; # ssl-certificate = cert-copy.certificate;
# ssl-private-key = cert-copy.private-key; # ssl-private-key = cert-copy.private-key;
# }; # };
# dns = { # dns = {
# default-host = primary-ip; # default-host = primary-ip;
# listen-ip = primary-ip; # listen-ip = primary-ip;
# mail-hosts = [ "mail.fudo.org" ]; # mail-hosts = [ "mail.fudo.org" ];
# }; # };
# chat = { # chat = {
# chat-hostname = "chat.fudo.org"; # chat-hostname = "chat.fudo.org";
# mail-server = "mail.fudo.org"; # mail-server = "mail.fudo.org";
# database-host = "localhost"; # database-host = "localhost";
# }; # };
# }; # };
# minecraft-server = { # minecraft-server = {
# enable = true; # enable = true;
# package = pkgs.minecraft-current; # package = pkgs.minecraft-current;
# data-dir = "/state/minecraft/selbyland"; # data-dir = "/state/minecraft/selbyland";
# world-name = "selbyland"; # world-name = "selbyland";
# motd = "Welcome to the Selby Minecraft server."; # motd = "Welcome to the Selby Minecraft server.";
# }; # };
}; };
networking = { networking = {
@ -240,12 +237,10 @@ in {
}]; }];
}; };
extif0 = { extif0 = {
ipv4.addresses = [ ipv4.addresses = [{
{ address = primary-ip;
address = primary-ip; prefixLength = 28;
prefixLength = 28; }];
}
];
}; };
}; };
}; };

View File

@ -14,20 +14,25 @@ let
in { in {
config = { config = {
networking = { networking = {
enableIPv6 = true; enableIPv6 = false;
useDHCP = false;
nameservers = [ "1.1.1.1" ]; nameservers = [ "1.1.1.1" ];
defaultGateway = { defaultGateway = {
interface = "extif0"; interface = "enp5s0f0";
address = site.gateway-v4; address = site.gateway-v4;
}; };
interfaces = { interfaces = {
extif0 = { enp5s0f0 = {
ipv4.addresses = [{ ipv4.addresses = [{
address = primary-ip; address = primary-ip;
prefixLength = 28; prefixLength = 28;
}]; }];
}; };
}; };
firewall = {
enable = false;
interfaces."podman+".allowedUDPPorts = [ 53 ];
};
}; };
systemd = { systemd = {
@ -80,17 +85,19 @@ in {
arion.backend = "podman-socket"; arion.backend = "podman-socket";
}; };
services.lemmyDocker.state-directory = "/state/services/lemmy";
fudo = { fudo = {
client.dns = { client.dns = {
ipv4 = true; ipv4 = true;
ipv6 = true; ipv6 = true;
user = "fudo-client"; user = "fudo-client";
external-interface = "extif0"; external-interface = "enp5s0f0";
}; };
mail.state-directory = "/state/services/mail"; mail.state-directory = "/state/services/mail";
nsd.zones."fudo.org".outgoingInterface = "extif0"; nsd.zones."fudo.org".outgoingInterface = "enp5s0f0";
# Necessary because germany isn't the default yet # Necessary because germany isn't the default yet
postgresql = { postgresql = {
@ -106,6 +113,7 @@ in {
authoritative-dns.state-directory = "/state/services/dns"; authoritative-dns.state-directory = "/state/services/dns";
jabber.state-directory = "/state/services/jabber"; jabber.state-directory = "/state/services/jabber";
logging.loki.state-directory = "/state/services/loki"; logging.loki.state-directory = "/state/services/loki";
mail-server.state-directory = "/state/services/mail";
metrics = { metrics = {
prometheus.state-directory = "/state/services/prometheus"; prometheus.state-directory = "/state/services/prometheus";
grafana.state-directory = "/state/services/grafana"; grafana.state-directory = "/state/services/grafana";
@ -117,6 +125,7 @@ in {
principals = [ "postgres/${hostFqdn}" ]; principals = [ "postgres/${hostFqdn}" ];
}; };
}; };
matrix.state-directory = "/state/services/matrix";
}; };
}; };
}; };

View File

@ -41,7 +41,7 @@ in {
fonts.fontconfig = { fonts.fontconfig = {
hinting = { hinting = {
enable = true; enable = true;
style = "hintfull"; style = "full";
}; };
subpixel.lcdfilter = "default"; subpixel.lcdfilter = "default";
antialias = true; antialias = true;

View File

@ -35,9 +35,9 @@ in {
security.acme.defaults.email = "admin@legatus.fudo.org"; security.acme.defaults.email = "admin@legatus.fudo.org";
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
"L /etc/adjtime - - - - /state/etc/adjtime" "L /etc/adjtime - - - - /state/etc/adjtime"
"d /state/services/podman/volumes 0700 root root - -" "d /state/services/podman/volumes 0700 root root - -"
"d /state/services/acme 0755 acme acme - -" "d /state/services/acme 0755 acme acme - -"
]; ];
fileSystems = { fileSystems = {
@ -49,8 +49,6 @@ in {
environment.systemPackages = local-packages; environment.systemPackages = local-packages;
# networking.firewall.allowedTCPPorts = [ 80 443 ];
# informis.cl-gemini = { # informis.cl-gemini = {
# enable = true; # enable = true;
@ -63,24 +61,8 @@ in {
services = { services = {
mastodonContainer.state-directory = "/state/services/mastodon"; 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"; authentikContainer.state-directory = "/state/services/authentik";
nextcloudContainer.state-directory = "/state/services/nextcloud"; nextcloudContainer.state-directory = "/state/services/nextcloud";
nginx.virtualHosts."fudo.org" = {
enableACME = true;
forceSSL = true;
};
}; };
virtualisation = { virtualisation = {
@ -98,12 +80,8 @@ in {
services = { services = {
auth = { auth = {
ldap.state-directory = "/state/auth/ldap"; ldap.state-directory = "/state/services/ldap";
kerberos = { kerberos.state-directory = "/state/services/heimdal-kdc";
state-directory = "/state/auth/kerberos";
# master-key-file = host-secrets.heimdal-master-key.target-file;
# ipropd-keytab = host-secrets.heimdal-ipropd-keytab.target-file;
};
}; };
chat = { chat = {
@ -112,40 +90,6 @@ in {
}; };
nexus.dns-server.listen-addresses = [ host-ipv4 ]; 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;
in {
# postgres-keytab = {
# source-file = files.service-keytabs.procul.postgres;
# target-file = "/srv/postgres/secure/postgres.keytab";
# user = "root";
# };
# gitea-database-password = {
# source-file = files.service-passwords.procul.gitea-database;
# target-file = "/srv/gitea/secure/database.passwd";
# user = config.fudo.git.user;
# };
# heimdal-master-key = {
# source-file = files.realm-master-keys."FUDO.ORG";
# target-file = "/run/heimdal/master-key";
# user = config.fudo.auth.kdc.user;
# };
# heimdal-ipropd-keytab = {
# source-file = files.service-keytabs.legatus.ipropd;
# target-file = "/run/heimdal/ipropd.keytab";
# user = config.fudo.auth.kdc.user;
# };
}; };
client.dns = { client.dns = {

View File

@ -58,8 +58,12 @@ in {
# }; # };
firewall = { firewall = {
enable = true;
allowedTCPPorts = [ 80 443 25565 config.services.murmur.port ]; allowedTCPPorts = [ 80 443 25565 config.services.murmur.port ];
allowedUDPPorts = [ 25565 34197 ]; allowedUDPPorts = [ 25565 34197 ];
extraCommands = ''
iptables -A INPUT -s 141.98.7.36 -j DROP
'';
}; };
nat.forwardPorts = [ nat.forwardPorts = [

View File

@ -75,20 +75,10 @@ in {
"export-projects.mount" "export-projects.mount"
]; ];
}; };
grafana = {
requires = [ "postgresql.service" ];
bindsTo = [ "postgresql.service" ];
};
}; };
}; };
fudo = let fudo = let host-secrets = config.fudo.secrets.host-secrets."${hostname}";
grafana-database-passwd-file = pkgs.lib.passwd.stablerandom-passwd-file
"grafana-database-nostromo-password"
"grafana-database-nostromo-password-${config.instance.build-seed}";
host-secrets = config.fudo.secrets.host-secrets.${hostname};
in { in {
secrets.host-secrets.${hostname} = { secrets.host-secrets.${hostname} = {
pricebot-auth-token = { pricebot-auth-token = {
@ -117,7 +107,6 @@ in {
metrics.grafana = { metrics.grafana = {
state-directory = "/state/services/grafana"; state-directory = "/state/services/grafana";
smtp.hostname = "mail.fudo.org"; smtp.hostname = "mail.fudo.org";
ldap.base-dn = "dc=fudo,dc=org";
}; };
postgresql = { postgresql = {
@ -135,8 +124,6 @@ in {
enable = true; enable = true;
local-networks = config.instance.local-networks; local-networks = config.instance.local-networks;
state-directory = "/state/services/postgresql"; state-directory = "/state/services/postgresql";
databases.grafana.users = config.instance.local-admins;
}; };
services.gitea-container = { services.gitea-container = {
@ -146,10 +133,6 @@ in {
state-directory = "/state/services/gitea"; state-directory = "/state/services/gitea";
trusted-networks = config.instance.local-networks; trusted-networks = config.instance.local-networks;
openid-urls = [ "https://authentik.fudo.org/" ]; openid-urls = [ "https://authentik.fudo.org/" ];
secret-key-file =
pkgs.lib.passwd.stablerandom-passwd-file "gitea-seattle-secret-key"
config.instance.build-seed;
networking = { networking = {
interface = "eno2"; interface = "eno2";
ipv4 = { ipv4 = {

View File

@ -193,16 +193,5 @@ in {
# loadLatestSave = true; # loadLatestSave = true;
# package = pkgs.factorio-headless-experimental; # package = pkgs.factorio-headless-experimental;
# }; # };
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";
};
};
}; };
} }

View File

@ -138,6 +138,7 @@ in {
groups = { groups = {
wheel.members = [ "niten" "reaper" ]; wheel.members = [ "niten" "reaper" ];
dns = { members = [ "niten" "reaper" "named" ]; }; dns = { members = [ "niten" "reaper" "named" ]; };
fudo.members = [ "niten" "reaper" ];
}; };
}; };

View File

@ -10,17 +10,23 @@ let
config = { config = {
containers.tester = { containers.tester = {
autoStart = true; autoStart = true;
# hostAddress = "10.0.0.14"; # localAddress = "10.0.0.14";
additionalCapabilities = [ "CAP_NET_ADMIN" ]; additionalCapabilities = [ "CAP_NET_ADMIN" ];
# privateNetwork = true; # privateNetwork = true;
macvlans = [ "enp7s0" ]; macvlans = [ "enp7s0" ];
# hostBridge = "tester0"; #hostBridge = "tester0";
#hostAddress = "10.0.0.14";
#privateNetwork = true;
config = { config = {
imports = [ pkgs.moduleRegistry.authoritativeDns ]; imports = [ pkgs.moduleRegistry.authoritativeDns ];
services.openssh.enable = true; services.openssh.enable = true;
users.users.niten = config.users.users.niten; users = let groupName = config.users.users.niten.group;
in {
users.niten = config.users.users.niten;
groups."${groupName}" = config.users.groups."${groupName}";
};
services.authoritative-dns = { services.authoritative-dns = {
enable = true; enable = true;
@ -40,12 +46,16 @@ let
allowedTCPPorts = [ 22 53 ]; allowedTCPPorts = [ 22 53 ];
allowedUDPPorts = [ 53 ]; allowedUDPPorts = [ 53 ];
}; };
interfaces = { # interfaces.eth0 = {
mv-enp7s0.ipv4.addresses = [{ # ipv4.addresses = [{
address = "10.0.0.14"; # address = "10.0.0.14";
prefixLength = 24; # prefixLength = 24;
}]; # }];
}; # };
interfaces.mv-enp7s0.ipv4.addresses = [{
address = "10.0.0.14";
prefixLength = 24;
}];
}; };
}; };
}; };
@ -118,7 +128,7 @@ in {
fonts.fontconfig = { fonts.fontconfig = {
hinting = { hinting = {
enable = true; enable = true;
style = "hintfull"; style = "full";
}; };
subpixel.lcdfilter = "default"; subpixel.lcdfilter = "default";
antialias = true; antialias = true;
@ -132,7 +142,7 @@ in {
hardware = { hardware = {
bluetooth = { bluetooth = {
enable = true; enable = true;
package = pkgs.bluezFull; package = pkgs.bluez;
}; };
xpadneo.enable = true; xpadneo.enable = true;
}; };

View File

@ -20,6 +20,7 @@ in {
prefixLength = 16; prefixLength = 16;
}]; }];
}; };
firewall.enable = false;
}; };
security.sudo.extraConfig = '' security.sudo.extraConfig = ''

View File

@ -47,8 +47,6 @@ in {
}; };
}; };
nixpkgs.config.allowUnfree = true;
hardware.enableAllFirmware = true; hardware.enableAllFirmware = true;
services = { services = {

View File

@ -46,6 +46,7 @@ let
imports = [ pkgs.moduleRegistry.authoritativeDns ]; imports = [ pkgs.moduleRegistry.authoritativeDns ];
nixpkgs.pkgs = pkgs; nixpkgs.pkgs = pkgs;
networking = { networking = {
enableIPv6 = false;
defaultGateway = { defaultGateway = {
address = getSiteGatewayV4 siteName; address = getSiteGatewayV4 siteName;
interface = "mv-${cfg.container.interface}"; interface = "mv-${cfg.container.interface}";
@ -57,8 +58,7 @@ let
}; };
interfaces."mv-${cfg.container.interface}" = { interfaces."mv-${cfg.container.interface}" = {
ipv4.addresses = optional (nameserverDeets.ipv4-address != null) { ipv4.addresses = optional (nameserverDeets.ipv4-address != null) {
address = trace "IP ADDRESS: ${nameserverDeets.ipv4-address}" address = nameserverDeets.ipv4-address;
nameserverDeets.ipv4-address;
prefixLength = getSiteV4PrefixLength siteName; prefixLength = getSiteV4PrefixLength siteName;
}; };
ipv6.addresses = optional (nameserverDeets.ipv6-address != null) { ipv6.addresses = optional (nameserverDeets.ipv6-address != null) {
@ -224,6 +224,7 @@ in {
}; };
}; };
}); });
default = null;
}; };
enable-notifications = enable-notifications =
@ -283,7 +284,6 @@ in {
nameValuePair (zoneKeySecret zone) { nameValuePair (zoneKeySecret zone) {
source-file = zoneCfg.ksk.private-key; source-file = zoneCfg.ksk.private-key;
target-file = "/run/nsd/${baseNameOf 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); }) (filterAttrs (_: zoneCfg: zoneCfg.ksk != null) cfg.zones);
zones = mapAttrs (zone-name: zoneCfg: zones = mapAttrs (zone-name: zoneCfg:

View File

@ -22,12 +22,6 @@ in {
description = "Path at which to store Gitea state."; description = "Path at which to store Gitea state.";
}; };
secret-key-file = mkOption {
type = str;
description =
"Path to file containing Gitea secret key, for encrypting secrets.";
};
trusted-networks = mkOption { trusted-networks = mkOption {
type = listOf str; type = listOf str;
description = description =
@ -131,7 +125,6 @@ in {
service.DISABLE_REGISTRATION = true; service.DISABLE_REGISTRATION = true;
security = { security = {
INSTALL_LOCK = true; INSTALL_LOCK = true;
SECRET_KEY = "file:${cfg.secret-key-file}";
LOGIN_REMEMBER_DAYS = 30; LOGIN_REMEMBER_DAYS = 30;
}; };
metrics.ENABLED = cfg.trusted-networks != [ ]; metrics.ENABLED = cfg.trusted-networks != [ ];
@ -153,11 +146,12 @@ in {
}; };
openid = { openid = {
ENABLE_OPENID_SIGNIN = true; ENABLE_OPENID_SIGNIN = true;
WHITELISTED_URIS = cfg.openid-urls; WHITELISTED_URIS = concatStringsSep "," cfg.openid-urls;
}; };
oauth2_client = { oauth2_client = {
REGISTER_EMAIL_CONFIRM = false; REGISTER_EMAIL_CONFIRM = false;
OPENID_CONNECT_SCOPES = [ "email" "profile" ]; OPENID_CONNECT_SCOPES =
concatStringsSep "," [ "email" "profile" ];
ENABLE_AUTO_REGISTRATION = true; ENABLE_AUTO_REGISTRATION = true;
USERNAME = "email"; USERNAME = "email";
UPDATE_AVATAR = true; UPDATE_AVATAR = true;
@ -194,11 +188,14 @@ in {
# enableACME = true; # enableACME = true;
# forceSSL = true; # forceSSL = true;
locations."/".proxyPass = "http://127.0.0.1:8080"; locations."/".proxyPass = "http://127.0.0.1:8080";
locations."/metrics" = mkIf (cfg.trusted-networks != [ ]) (let locations."/metrics" = mkIf (cfg.trusted-networks != [ ]) {
networkAllowClauses = proxyPass = "http://127.0.0.1:8080/metrics";
map (net: "allow ${net};") cfg.trusted-networks; extraConfig = let
in concatStringsSep "\n" networkAllowClauses =
(networkAllowClauses ++ [ "deny all;" ])); map (net: "allow ${net};") cfg.trusted-networks;
in concatStringsSep "\n"
(networkAllowClauses ++ [ "deny all;" ]);
};
}; };
}; };
}; };

View File

@ -20,7 +20,7 @@ let
isLocalMailserver = domain-name == mailserver-domain-name; isLocalMailserver = domain-name == mailserver-domain-name;
metricsEnabled = mailserver-domain.prometheus-hosts != [ ]; metricsEnabled = !isNull mailserver-domain.metrics;
host-certs = config.fudo.acme.host-domains.${hostname}; host-certs = config.fudo.acme.host-domains.${hostname};
@ -28,13 +28,20 @@ in {
options.fudo.services.mail-server = with types; { options.fudo.services.mail-server = with types; {
debug = mkEnableOption "Enable debug options for mailserver."; debug = mkEnableOption "Enable debug options for mailserver.";
enable = mkOption {
type = bool;
default = true;
description =
"Temporary -- allow disabling mail server (in favor of mail container).";
};
state-directory = mkOption { state-directory = mkOption {
type = str; type = str;
description = "Directory at which to store mailserver state."; description = "Directory at which to store mailserver state.";
}; };
}; };
config = mkIf hasMailServer { config = mkIf (hasMailServer && cfg.enable) {
services.nginx = mkIf (isMailServer && metricsEnabled) { services.nginx = mkIf (isMailServer && metricsEnabled) {
enable = true; enable = true;
recommendedOptimisation = true; recommendedOptimisation = true;
@ -106,17 +113,6 @@ in {
in { in {
aliases = mkIf metricsEnabled { mail-stats = "${mailserver-fqdn}."; }; aliases = mkIf metricsEnabled { mail-stats = "${mailserver-fqdn}."; };
# 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;
# smtp = srv-record mailserver-fqdn 25;
# submission = srv-record mailserver-fqdn 587;
# };
metric-records = mkIf metricsEnabled metric-records = mkIf metricsEnabled
(genAttrs [ "dovecot" "postfix" "rspamd" ] (genAttrs [ "dovecot" "postfix" "rspamd" ]
(_: srv-record "mail-stats" 443)); (_: srv-record "mail-stats" 443));

View File

@ -3,54 +3,28 @@
with lib; with lib;
let let
hostname = config.instance.hostname; hostname = config.instance.hostname;
domain-name = config.fudo.hosts."${hostname}".domain; domainName = config.fudo.hosts."${hostname}".domain;
domain = config.fudo.domains."${domain-name}"; domain = config.fudo.domains."${domainName}";
host-secrets = config.fudo.secrets.host-secrets."${hostname}"; inherit (pkgs.lib) getHostIpv4 getHostIpv6 getHostFqdn;
notEmpty = lst: (length lst) > 0; hostSecrets = config.fudo.secrets.host-secrets."${hostname}";
metricsEnabled = notEmpty domain.prometheus-hosts; metricsEnabled = !isNull domain.metrics;
metricsScraper = elem hostname domain.prometheus-hosts; isPrometheus = hostname == domain.metrics.prometheus-host;
metricsMonitor = elem hostname domain.grafana-hosts; isGrafana = hostname == domain.metrics.grafana-host;
prometheus-cfg = config.fudo.services.metrics.prometheus; grafanaHost = domain.metrics.grafana-host;
grafana-cfg = config.fudo.services.metrics.grafana; prometheusHost = domain.metrics.prometheus-host;
host-fqdn = hostname: prometheusCfg = config.fudo.services.metrics.prometheus;
let host-domain = config.fudo.hosts.${hostname}.domain; grafanaCfg = config.fudo.services.metrics.grafana;
in "${hostname}.${host-domain}"; privateNetwork = config.fudo.services.metrics.private-network;
host-auth-fqdn = hostname: "${host-fqdn hostname}.";
make-alias-map = type: hosts:
listToAttrs
(imap0 (i: hostname: nameValuePair hostname "${type}-${toString i}") hosts);
headOrNull = lst: if notEmpty lst then head lst else null;
metrics-master = headOrNull domain.prometheus-hosts;
monitor-master = headOrNull domain.grafana-hosts;
metrics-alias-map = make-alias-map "metrics" domain.prometheus-hosts;
monitor-alias-map = make-alias-map "monitor" domain.grafana-hosts;
alias-map-to-cnames =
mapAttrs' (hostname: alias: nameValuePair alias (host-auth-fqdn hostname));
alias-map-to-hostnames =
mapAttrsToList (hostname: alias: "${alias}.${domain-name}");
grafana-smtp-password-file = grafana-smtp-password-file =
pkgs.lib.passwd.stablerandom-passwd-file "grafana-smtp-passwd" pkgs.lib.passwd.stablerandom-passwd-file "grafana-smtp-passwd"
config.instance.build-seed; config.instance.build-seed;
grafana-auth-password-file =
pkgs.lib.passwd.stablerandom-passwd-file "grafana-auth-passwd"
config.instance.build-seed;
grafana-admin-password-file = grafana-admin-password-file =
pkgs.lib.passwd.stablerandom-passwd-file "grafana-admin-passwd" pkgs.lib.passwd.stablerandom-passwd-file "grafana-admin-passwd"
config.instance.build-seed; config.instance.build-seed;
@ -59,25 +33,14 @@ let
pkgs.lib.passwd.stablerandom-passwd-file "grafana-secret-key" pkgs.lib.passwd.stablerandom-passwd-file "grafana-secret-key"
config.instance.build-seed; config.instance.build-seed;
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 { in {
options.fudo.services.metrics = with types; { options.fudo.services.metrics = with types; {
private-network = mkOption {
type = bool;
description = "Network is private, encryption not required.";
default = false;
};
prometheus = { prometheus = {
static-targets = mkOption { static-targets = mkOption {
type = attrsOf (listOf str); type = attrsOf (listOf str);
@ -86,6 +49,7 @@ in {
example = { dovecot = [ "my.host.name:1111" ]; }; example = { dovecot = [ "my.host.name:1111" ]; };
default = { }; default = { };
}; };
state-directory = mkOption { state-directory = mkOption {
type = str; type = str;
description = "Path at which to store Prometheus state."; description = "Path at which to store Prometheus state.";
@ -99,6 +63,7 @@ in {
description = "Username from which to send Grafana alerts."; description = "Username from which to send Grafana alerts.";
default = "monitor"; default = "monitor";
}; };
hostname = mkOption { hostname = mkOption {
type = str; type = str;
description = "Hostname of the SMTP host."; description = "Hostname of the SMTP host.";
@ -106,50 +71,35 @@ in {
}; };
}; };
ldap = let base-dn = domainToBaseDn config.instance.local-domain; oauth = let
in { oauthOpts.options = {
base-dn = mkOption { hostname = mkOption {
type = str; type = str;
description = "DN under which to search for users."; description = "Host of the OAuth server.";
default = base-dn; };
};
bind-user = mkOption { client-id = mkOption {
type = str; type = str;
description = "DN as which to bind to the LDAP server."; description =
default = "grafana_reader"; "Path to file containing the Grafana OAuth client ID.";
}; };
bind-passwd = mkOption { client-secret = mkOption {
type = nullOr str; type = str;
description = "Path to file with bind password. Generated if null."; description =
default = null; "Path to file containing the Grafana OAuth client secret.";
};
slug = mkOption {
type = str;
description = "The application slug on the OAuth server.";
};
}; };
in mkOption {
type = nullOr (submodule oauthOpts);
default = null;
}; };
# database = {
# hostname = mkOption {
# type = str;
# description = "Hostname of the postgresql database.";
# default = "localhost";
# };
# user = mkOption {
# type = str;
# description =
# "Username as which to authenticate to the postgresql database.";
# };
# password-file = mkOption {
# type = str;
# description =
# "Password file (on the target host) which to authenticate to the postgresql database.";
# };
# name = mkOption {
# type = str;
# description = "Database name.";
# default = "grafana";
# };
# };
state-directory = mkOption { state-directory = mkOption {
type = str; type = str;
description = "Path at which to store Grafana state."; description = "Path at which to store Grafana state.";
@ -159,189 +109,131 @@ in {
}; };
config = mkIf metricsEnabled { config = mkIf metricsEnabled {
fudo = { fudo = {
system-users = {
"${grafana-cfg.smtp.username}" = {
description = "Grafana Alerts";
ldap-hashed-password =
pkgs.lib.passwd.hash-ldap-passwd "grafana-smtp-passwd"
grafana-smtp-password-file;
};
"${grafana-cfg.ldap.bind-user}" = mkIf ((domain.ldap-servers != [ ])
&& (grafana-cfg.ldap.bind-passwd == null)) {
description = "Grafana Authentication Reader";
ldap-hashed-password =
pkgs.lib.passwd.hash-ldap-passwd "grafana-auth-passwd"
grafana-auth-password-file;
};
};
secrets.host-secrets = secrets.host-secrets =
let grafana-user = config.systemd.services.grafana.serviceConfig.User; let grafana-user = config.systemd.services.grafana.serviceConfig.User;
in { in {
"${hostname}" = { "${hostname}" = {
grafana-smtp-password = mkIf metricsMonitor { grafana-admin-password = mkIf isGrafana {
source-file = grafana-smtp-password-file;
target-file = "/run/metrics/grafana/smtp.passwd";
user = grafana-user;
};
grafana-admin-password = mkIf metricsMonitor {
source-file = grafana-admin-password-file; source-file = grafana-admin-password-file;
target-file = "/run/metrics/grafana/admin.passwd"; target-file = "/run/metrics/grafana/admin.passwd";
user = grafana-user; user = grafana-user;
}; };
grafana-secret-key = mkIf metricsMonitor { grafana-secret-key = mkIf isGrafana {
source-file = grafana-secret-key-file; source-file = grafana-secret-key-file;
target-file = "/run/metrics/grafana/secret.key"; target-file = "/run/metrics/grafana/secret.key";
user = grafana-user; user = grafana-user;
}; };
grafana-postgresql-password = mkIf metricsMonitor { grafana-client-id = mkIf (isGrafana && !isNull grafanaCfg.oauth) {
source-file = grafana-database-password-file; source-file = grafanaCfg.oauth.client-id;
target-file = "/run/metrics/grafana/postgres.passwd"; target-file = "/run/metrics/grafana/oauth-client-id";
user = grafana-user; user = grafana-user;
}; };
postgresql-grafana-password = mkIf isPostgresServer { grafana-client-secret =
source-file = grafana-database-password-file; mkIf (isGrafana && !isNull grafanaCfg.oauth) {
target-file = "/run/postgres-users/grafana.passwd"; source-file = grafanaCfg.oauth.client-secret;
user = config.systemd.services.postgresql.serviceConfig.User; target-file = "/run/metrics/grafana/oauth-client-secret";
}; user = grafana-user;
};
}; };
}; };
zones."${domain.zone}" = { zones."${domain.zone}" = {
aliases = let hosts = {
metrics-aliases = alias-map-to-cnames metrics-alias-map; grafana = {
monitor-aliases = alias-map-to-cnames monitor-alias-map; ipv4-address = getHostIpv4 grafanaHost;
metrics-master-cname = optionalAttrs (metrics-master != null) { ipv6-address = getHostIpv6 grafanaHost;
metrics = "${metrics-master}.${domain-name}."; description = "Grafana Metrics Analysis on ${grafanaHost}.";
}; };
monitor-master-cname = optionalAttrs (monitor-master != null) { prometheus = {
monitor = "${monitor-master}.${domain-name}."; ipv4-address = getHostIpv4 prometheusHost;
ipv6-address = getHostIpv6 prometheusHost;
description = "Prometheus Metrics Aggregator on ${prometheusHost}.";
}; };
in metrics-aliases // monitor-aliases // metrics-master-cname };
// monitor-master-cname;
aliases = {
metrics = "prometheus.${domainName}";
monitor = "grafana.${domainName}";
};
metric-records = let metric-records = let
domain-hosts = filterAttrs (hostname: hostOpts: domainHosts = filterAttrs (hostname: hostOpts:
hostOpts.domain == domain-name && hostOpts.nixos-system) hostOpts.domain == domainName && hostOpts.nixos-system)
config.fudo.hosts; config.fudo.hosts;
in { in {
node = map (hostname: { node = map (hostname: {
host = "${hostname}.${domain-name}"; host = getHostFqdn hostname;
port = if is-private-network then 80 else 443; port = if privateNetwork then 80 else 443;
}) (attrNames domain-hosts); }) (attrNames domainHosts);
}; };
}; };
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 = { metrics = {
node-exporter = { node-exporter = {
enable = true; enable = true;
hostname = host-fqdn hostname; hostname = getHostFqdn hostname;
private-network = is-private-network; private-network = privateNetwork;
}; };
prometheus = mkIf metricsScraper { prometheus = mkIf isPrometheus {
enable = true; enable = true;
service-discovery-dns = { service-discovery-dns = {
node = [ "node._metrics._tcp.${domain-name}" ]; node = [ "node._metrics._tcp.${domainName}" ];
}; };
static-targets = prometheus-cfg.static-targets; static-targets = prometheusCfg.static-targets;
hostname = let alias = metrics-alias-map.${hostname}; hostname = "prometheus.${domainName}";
in "${alias}.${domain-name}"; state-directory = prometheusCfg.state-directory;
state-directory = prometheus-cfg.state-directory; private-network = privateNetwork;
private-network = is-private-network;
}; };
};
grafana = mkIf metricsMonitor { services.grafana = mkIf isGrafana {
enable = true; enable = true;
hostname = let alias = monitor-alias-map.${hostname}; state-directory = grafanaCfg.state-directory;
in "${alias}.${domain-name}"; base-url = let scheme = if privateNetwork then "http" else "https";
smtp = let cfg = grafana-cfg.smtp; in "${scheme}://grafana.${domainName}";
in { admin-password-file = hostSecrets.grafana-admin-password.target-file;
username = cfg.username; secret-key-file = hostSecrets.grafana-secret-key.target-file;
password-file = host-secrets.grafana-smtp-password.target-file; datasources = {
hostname = cfg.hostname; "${domainName}" = {
email = "${cfg.username}@${domain-name}"; url = let scheme = if privateNetwork then "http" else "https";
in "${scheme}://prometheus.${domainName}";
type = "prometheus";
default = true;
}; };
database = let cfg = grafana-cfg.database; };
in { oauth = mkIf (!isNull grafanaCfg.oauth) {
name = "grafana"; inherit (grafanaCfg.oauth) hostname slug;
user = "grafana"; client-id = hostSecrets.grafana-client-id.target-file;
password-file = client-secret = hostSecrets.grafana-client-secret.target-file;
host-secrets.grafana-postgresql-password.target-file;
hostname = postgresServer;
};
ldap = mkIf (domain.ldap-servers != [ ]) {
hosts = map host-fqdn domain.ldap-servers;
base-dn = grafana-cfg.ldap.base-dn;
bind-dn =
"cn=${grafana-cfg.ldap.bind-user},${grafana-cfg.ldap.base-dn}";
bind-passwd = if (grafana-cfg.ldap.bind-passwd != null) then
grafana-cfg.ldap.bind-passwd
else
(readFile grafana-auth-password-file);
};
admin-password-file = host-secrets.grafana-admin-password.target-file;
secret-key-file = host-secrets.grafana-secret-key.target-file;
datasources = let
scheme = if is-private-network then "http" else "https";
host-config = hostname: {
url = "${scheme}://${hostname}.${domain-name}";
type = "prometheus";
default = hostname == "metrics-0";
};
in listToAttrs
(map (host: nameValuePair "prometheus-${host}" (host-config host))
(attrValues metrics-alias-map));
state-directory = grafana-cfg.state-directory;
private-network = is-private-network;
}; };
}; };
}; };
services.nginx = services.nginx = mkIf (isPrometheus || isGrafana) {
mkIf (hostname == metrics-master || hostname == monitor-master) { enable = true;
enable = true; recommendedOptimisation = true;
recommendedOptimisation = true; recommendedProxySettings = true;
recommendedProxySettings = true;
virtualHosts = virtualHosts = let scheme = if privateNetwork then "http" else "https";
let scheme = if is-private-network then "http" else "https"; in {
in { "metrics.${domainName}".locations."/".return =
"metrics.${domain-name}" = mkIf (hostname == metrics-master) { "302 http://prometheus.${domainName}";
enableACME = !is-private-network; "monitor.${domainName}".locations."/".return =
forceSSL = !is-private-network; "302 http://grafana.${domainName}";
locations."/".return = let alias = metrics-alias-map.${hostname};
in "301 ${scheme}://${alias}.${domain-name}$request_uri"; "grafana.${domainName}" = {
}; enableACME = !privateNetwork;
"monitor.${domain-name}" = mkIf (hostname == monitor-master) { forceSSL = !privateNetwork;
enableACME = !is-private-network; locations."/".proxyPass =
forceSSL = !is-private-network; "http://localhost:${toString config.fudo.services.grafana.port}";
locations."/".return = let alias = monitor-alias-map.${hostname}; };
in "301 ${scheme}://${alias}.${domain-name}$request_uri";
};
};
}; };
};
}; };
} }

View File

@ -3,14 +3,6 @@
with lib; with lib;
let local-domain = "sea.fudo.org"; let local-domain = "sea.fudo.org";
in { in {
# imports = [ ./seattle/authelia.nix ./seattle/keycloak.nix ];
imports = [
# (import ./seattle/authentik.nix {
# authentikHost = "nostromo";
# proxyHost = "limina";
# externalHostname = "authentik.fudo.link";
# })
];
config = { config = {
fudo = { fudo = {
@ -63,16 +55,6 @@ in {
}; };
fileSystems = { 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" = { "/mnt/music" = {
device = "doraemon.${local-domain}:/volume1/Music"; device = "doraemon.${local-domain}:/volume1/Music";
fsType = "nfs"; fsType = "nfs";
@ -83,10 +65,6 @@ in {
fsType = "nfs"; fsType = "nfs";
options = [ "comment=systemd.automount" ]; options = [ "comment=systemd.automount" ];
}; };
# fileSystems."/mnt/security" = {
# device = "panopticon.${local-domain}:/srv/kerberos/data";
# fsType = "nfs4";
# };
"/mnt/cargo_video" = { "/mnt/cargo_video" = {
device = "cargo.${local-domain}:/volume1/video"; device = "cargo.${local-domain}:/volume1/video";
fsType = "nfs4"; fsType = "nfs4";
@ -98,41 +76,21 @@ in {
options = [ "sec=krb5i" "x-systemd.automount" ]; options = [ "sec=krb5i" "x-systemd.automount" ];
}; };
# "proto=tcp"
# # NOTE: these are pointing directly to nostromo so the krb lookup works # # NOTE: these are pointing directly to nostromo so the krb lookup works
"/net/documents" = { "/net/documents" = {
device = "nostromo.${local-domain}:/export/documents"; device = "nostromo.${local-domain}:/export/documents";
fsType = "nfs4"; fsType = "nfs4";
options = [ options = [ "sec=krb5p" "x-systemd.automount" ];
"sec=krb5p"
"x-systemd.automount"
# "vers=4"
# "minorversion=2"
# "proto=tcp"
];
}; };
"/net/downloads" = { "/net/downloads" = {
device = "nostromo.${local-domain}:/export/downloads"; device = "nostromo.${local-domain}:/export/downloads";
fsType = "nfs4"; fsType = "nfs4";
options = [ options = [ "sec=krb5i" "x-systemd.automount" ];
"sec=krb5i"
"x-systemd.automount"
# "vers=4"
# "minorversion=2"
# "proto=tcp"
];
}; };
"/net/projects" = { "/net/projects" = {
device = "nostromo.${local-domain}:/export/projects"; device = "nostromo.${local-domain}:/export/projects";
fsType = "nfs4"; fsType = "nfs4";
options = [ options = [ "sec=krb5p" "x-systemd.automount" ];
"sec=krb5p"
"x-systemd.automount"
# "vers=4"
# "minorversion=2"
# "proto=tcp"
];
}; };
}; };

View File

@ -1,12 +1,10 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
with lib; with lib; {
{
config = let config = let
filterExistingUsers = users: group-members: filterExistingUsers = users: group-members:
let user-list = attrNames users; let user-list = attrNames users;
in filter (username: elem username user-list) in filter (username: elem username user-list) group-members;
group-members;
hostname = config.instance.hostname; hostname = config.instance.hostname;
host-cfg = config.fudo.hosts.${hostname}; host-cfg = config.fudo.hosts.${hostname};
@ -14,8 +12,8 @@ with lib;
sys = config.instance; sys = config.instance;
in { in {
fudo.auth.ldap-server = { fudo.auth.ldap-server = {
users = filterAttrs users =
(username: userOpts: userOpts.ldap-hashed-passwd != null) filterAttrs (username: userOpts: userOpts.ldap-hashed-passwd != null)
config.fudo.users; config.fudo.users;
groups = config.fudo.groups; groups = config.fudo.groups;
@ -29,14 +27,10 @@ with lib;
''; '';
environment.etc = mapAttrs' (username: userOpts: environment.etc = mapAttrs' (username: userOpts:
nameValuePair nameValuePair "ssh/private_keys.d/${username}" {
"ssh/private_keys.d/${username}" text = concatStringsSep "\n"
{ (map (keypair: readFile keypair.public-key) userOpts.ssh-keys);
text = concatStringsSep "\n" }) sys.local-users;
(map (keypair: readFile keypair.public-key)
userOpts.ssh-keys);
})
sys.local-users;
users = { users = {
users = mapAttrs (username: userOpts: { users = mapAttrs (username: userOpts: {
@ -47,8 +41,8 @@ with lib;
group = userOpts.primary-group; group = userOpts.primary-group;
home = if (userOpts.home-directory != null) then home = if (userOpts.home-directory != null) then
userOpts.home-directory userOpts.home-directory
else else
"/home/${userOpts.primary-group}/${username}"; "/home/${userOpts.primary-group}/${username}";
hashedPassword = userOpts.login-hashed-passwd; hashedPassword = userOpts.login-hashed-passwd;
openssh.authorizedKeys.keys = userOpts.ssh-authorized-keys; openssh.authorizedKeys.keys = userOpts.ssh-authorized-keys;
}) sys.local-users; }) sys.local-users;
@ -57,8 +51,7 @@ with lib;
gid = groupOpts.gid; gid = groupOpts.gid;
members = filterExistingUsers sys.local-users groupOpts.members; members = filterExistingUsers sys.local-users groupOpts.members;
}) sys.local-groups) // { }) sys.local-groups) // {
wheel = { members = sys.local-admins; }; wheel.members = sys.local-admins;
docker = mkIf (host-cfg.docker-server) { members = sys.local-admins; };
}; };
}; };
@ -73,9 +66,7 @@ with lib;
# Domain = local-domain; # Domain = local-domain;
"Local-Realms" = local-realm; "Local-Realms" = local-realm;
}; };
Translation = { Translation = { GSS-Methods = "static"; };
GSS-Methods = "static";
};
Static = let Static = let
generate-admin-entry = admin: userOpts: generate-admin-entry = admin: userOpts:
nameValuePair "${admin}/root@${local-realm}" "root"; nameValuePair "${admin}/root@${local-realm}" "root";
@ -84,8 +75,7 @@ with lib;
admin-entries = admin-entries =
mapAttrs' generate-admin-entry (getAttrs local-admins local-users); mapAttrs' generate-admin-entry (getAttrs local-admins local-users);
user-entries = user-entries = mapAttrs' generate-user-entry local-users;
mapAttrs' generate-user-entry local-users;
in admin-entries // user-entries; in admin-entries // user-entries;
}; };
@ -94,6 +84,7 @@ with lib;
groups-with-members = attrNames groups-with-members = attrNames
(filterAttrs (group: groupOpts: (length groupOpts.members) > 0) (filterAttrs (group: groupOpts: (length groupOpts.members) > 0)
sys.local-groups); sys.local-groups);
in map (group: "d /home/${group} 550 root ${group} - -") groups-with-members; in map (group: "d /home/${group} 550 root ${group} - -")
groups-with-members;
}; };
} }

View File

@ -5,7 +5,7 @@ with lib; {
users = { users = {
niten = { niten = {
uid = 10000; uid = 10000;
primary-group = "admin"; primary-group = "fudo";
common-name = "Peter Selby"; common-name = "Peter Selby";
given-name = "Peter"; given-name = "Peter";
surname = "Selby"; surname = "Selby";
@ -139,7 +139,7 @@ with lib; {
reaper = { reaper = {
uid = 10049; uid = 10049;
primary-group = "admin"; primary-group = "fudo";
common-name = "Jonathan Stewart"; common-name = "Jonathan Stewart";
given-name = "Jonathan"; given-name = "Jonathan";
surname = "Stewart"; surname = "Stewart";

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
description = "Fudo Host Configuration"; description = "Fudo Host Configuration";
inputs = { inputs = {
nixpkgs.url = "nixpkgs/nixos-23.05"; nixpkgs.url = "nixpkgs/nixos-23.11";
fudo-home = { fudo-home = {
url = "git+https://git.fudo.org/fudo-nix/home.git"; url = "git+https://git.fudo.org/fudo-nix/home.git";
@ -39,8 +39,6 @@
nixpkgsUnstable.url = "nixpkgs/nixos-unstable"; nixpkgsUnstable.url = "nixpkgs/nixos-unstable";
nixpkgs2111.url = "nixpkgs/nixos-21.11";
wallfly = { wallfly = {
url = "git+https://git.fudo.org/fudo-public/wallfly.git"; url = "git+https://git.fudo.org/fudo-public/wallfly.git";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
@ -136,6 +134,11 @@
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
grafana-module = {
url = "git+https://git.fudo.org/fudo-public/grafana-module.git";
inputs.nixpkgs.follows = "nixpkgs";
};
textfiles = { textfiles = {
url = "git+https://git.informis.land/informis/textfiles.git"; url = "git+https://git.informis.land/informis/textfiles.git";
flake = false; flake = false;
@ -143,11 +146,11 @@
}; };
outputs = { self, nixpkgs, fudo-home, fudo-lib, fudo-entities, fudo-pkgs outputs = { self, nixpkgs, fudo-home, fudo-lib, fudo-entities, fudo-pkgs
, fudo-secrets, chute, chuteUnstable, nixpkgsUnstable, nixpkgs2111, pricebot , fudo-secrets, chute, chuteUnstable, nixpkgsUnstable, pricebot, wallfly
, wallfly, objectifier, nexus, suanni, snooper, tattler, lemmy-docker , objectifier, nexus, suanni, snooper, tattler, lemmy-docker
, tesla-mate-container, mastodon-container, authentik-container , tesla-mate-container, mastodon-container, authentik-container
, nextcloud-container, textfiles, matrix-module, mail-server , nextcloud-container, textfiles, matrix-module, mail-server
, authoritative-dns, frigate-container, ... }@inputs: , authoritative-dns, frigate-container, grafana-module, ... }@inputs:
with nixpkgs.lib; with nixpkgs.lib;
let let
fudo-nixos-hosts = filterAttrs (hostname: hostOpts: hostOpts.nixos-system) fudo-nixos-hosts = filterAttrs (hostname: hostOpts: hostOpts.nixos-system)
@ -185,13 +188,7 @@
chute = chute.packages.${arch}.chute; chute = chute.packages.${arch}.chute;
chuteUnstable = chuteUnstable.packages.${arch}.chute; chuteUnstable = chuteUnstable.packages.${arch}.chute;
}) })
(final: prev: { (final: prev: { pkgsUnstable = unstable; })
pkgs2111 = import nixpkgs2111 {
system = arch;
config.allowUnfree = true;
};
pkgsUnstable = unstable;
})
(final: prev: { (final: prev: {
signal-desktop = unstable.signal-desktop; signal-desktop = unstable.signal-desktop;
factorio-experimental = unstable.factorio-experimental; factorio-experimental = unstable.factorio-experimental;
@ -234,6 +231,7 @@
mail-server.nixosModules.default mail-server.nixosModules.default
authoritative-dns.nixosModules.default authoritative-dns.nixosModules.default
frigate-container.nixosModules.default frigate-container.nixosModules.default
grafana-module.nixosModules.default
nexus.nixosModules.nexus-client nexus.nixosModules.nexus-client
nexus.nixosModules.nexus-server nexus.nixosModules.nexus-server