summaryrefslogtreecommitdiff
path: root/os/srv
diff options
context:
space:
mode:
authoradikro <adikro@disroot.org>2026-06-27 02:41:27 +0200
committeradikro <adikro@disroot.org>2026-06-27 02:41:27 +0200
commit9b8f9a4bf5e13efe49ec101f127d0df3d7bb3cf3 (patch)
treeb857cd260647dea82b0c63367eef9fa18cd63b8d /os/srv
parent30f34740891096471b5663704fbbd9bf67ebe885 (diff)
revamped sanoid, syncoid and nfs
Diffstat (limited to 'os/srv')
-rw-r--r--os/srv/authelia.nix152
-rw-r--r--os/srv/backup.nix110
-rw-r--r--os/srv/cluster.nix16
-rw-r--r--os/srv/crowdsec.nix81
-rw-r--r--os/srv/default.nix4
-rw-r--r--os/srv/gaming.nix27
-rw-r--r--os/srv/headscale.nix40
-rw-r--r--os/srv/i2p.nix98
-rw-r--r--os/srv/lldap.nix52
-rw-r--r--os/srv/monero.nix88
-rw-r--r--os/srv/nfs.nix13
-rw-r--r--os/srv/nginx.nix2
-rw-r--r--os/srv/postgres.nix72
-rw-r--r--os/srv/redis.nix37
-rw-r--r--os/srv/restic.nix12
-rw-r--r--os/srv/wireguard.nix8
-rw-r--r--os/srv/zfs.nix48
17 files changed, 690 insertions, 170 deletions
diff --git a/os/srv/authelia.nix b/os/srv/authelia.nix
index 52b7114..2c42b0a 100644
--- a/os/srv/authelia.nix
+++ b/os/srv/authelia.nix
@@ -2,48 +2,146 @@
config,
lib,
masterDomain,
+ securityTemplates,
...
}:
let
cfg = config.os.srv.authelia;
+ computedBaseDN = lib.concatStringsSep "," (
+ map (domainPart: "dc=${domainPart}") (lib.splitString "." masterDomain)
+ );
in
{
- options.os.srv.authelia = {
- enable = lib.mkEnableOption "enables authelia scanning";
- extraRules = lib.mkOption {
- type = lib.types.listOf lib.types.attrs;
- default = [ ];
- description = "Additional access control rules to be appended to Authelia.";
- };
- };
+ options.os.srv.authelia.enable =
+ lib.mkEnableOption "enables authelia authentication gateway instance";
+
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = config.os.srv.sops.enable;
- message = "Required for password secure password storing";
+ message = "sops must be enabled for secure cryptographic token storage";
}
{
- assertion = config.os.srv.lldap.enable;
- message = "required for user accounts";
+ assertion = config.os.core.network.enableFirewall;
+ message = "Requires firewall";
}
];
+
+ sops.secrets = {
+ "authelia/jwt_secret" = {
+ owner = "authelia-main";
+ group = "authelia-main";
+ restartUnits = [ "authelia-main.service" ];
+ };
+ "authelia/session_secret" = {
+ owner = "authelia-main";
+ group = "authelia-main";
+ restartUnits = [ "authelia-main.service" ];
+ };
+ "authelia/encryption_key" = {
+ owner = "authelia-main";
+ group = "authelia-main";
+ restartUnits = [ "authelia-main.service" ];
+ };
+
+ "authelia/oidc_hmac" = {
+ owner = "authelia-main";
+ group = "authelia-main";
+ restartUnits = [ "authelia-main.service" ];
+ };
+ "authelia/oidc_private_key" = {
+ owner = "authelia-main";
+ group = "authelia-main";
+ restartUnits = [ "authelia-main.service" ];
+ };
+
+ "postgres/authelia_password" = {
+ owner = "authelia-main";
+ group = "authelia-main";
+ restartUnits = [ "authelia-main.service" ];
+ };
+ "redis/password" = {
+ owner = "authelia-main";
+ group = "authelia-main";
+ restartUnits = [ "authelia-main.service" ];
+ };
+ };
+
services.authelia.instances.main = {
enable = true;
+
secrets = {
jwtSecretFile = config.sops.secrets."authelia/jwt_secret".path;
- storageEncryptionKeyFile = config.sops.secrets."authelia/encryptionKey".path;
+ sessionSecretFile = config.sops.secrets."authelia/session_secret".path;
+ storageEncryptionKeyFile = config.sops.secrets."authelia/encryption_key".path;
+
+ oidcHmacSecretFile = config.sops.secrets."authelia/oidc_hmac".path;
+ oidcIssuerPrivateKeyFile = config.sops.secrets."authelia/oidc_private_key".path;
};
+
settings = {
theme = "dark";
+ default_2fa_method = "totp";
+
+ log = {
+ level = "info";
+ format = "json";
+ path = "/var/log/authelia/authelia.log";
+ keep_stdout = true;
+ };
+
+ server.address = "tcp://127.0.0.1:9091";
+
+ telemetry.metrics = {
+ enabled = true;
+ address = "tcp://127.0.0.1:9959";
+ };
+
+ storage = {
+ postgres = {
+ host = config.os.core.network.ips.database-vm;
+ port = 5432;
+ database = "authelia";
+ username = "authelia";
+ timeout = "5s";
+ schema = "public";
+ };
+ };
+
+ session = {
+ name = "authelia_session";
+ expiration = "1h";
+ inactivity = "15m";
+ remember_me = "1M";
+ provider = {
+ redis = {
+ host = config.os.core.network.ips.database-vm;
+ port = 6379;
+ database = 0;
+ timeout = "5s";
+ };
+ };
+ };
+
authentication_backend = {
ldap = {
- address = "ldap://127.0.0.1:3890";
+ address = "ldap://${config.os.core.network.ips.gateway-vm}:3890";
implementation = "lldap";
- base_dn = "dc=example,dc=com";
- user = "uid=authelia,ou=people,dc=example,dc=com";
- password_file = config.sops.secrets."lldap/bind_password".path;
+ base_dn = computedBaseDN;
+ user = "uid=authelia,ou=people,${computedBaseDN}";
};
};
+
+ identity_providers = {
+ oidc = {
+ cors.allowed_origins = map (domain: "https://${domain}") (
+ builtins.attrNames config.os.cluster.nginxProxies
+ );
+
+ clients = config.os.cluster.oidcClients;
+ };
+ };
+
access_control = {
default_policy = "deny";
rules = [
@@ -52,10 +150,30 @@ in
policy = "bypass";
}
]
- ++ cfg.extraRules;
+ ++ config.os.cluster.autheliaRules;
};
+
session.domain = masterDomain;
};
+
+ environmentVariables = {
+ AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE = config.sops.secrets."lldap/password".path;
+ AUTHELIA_SESSION_REDIS_PASSWORD_FILE = config.sops.secrets."redis/password".path;
+ AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE = config.sops.secrets."postreg/authelia_password".path;
+ };
+ };
+
+ os.cluster.nginxProxies."auth.${masterDomain}" = {
+ enableACME = true;
+ forceSSL = true;
+ locations."/" = {
+ proxyPass = "http://${config.os.core.network.ips.gateway-vm}:9091";
+ extraConfig = securityTemplates.restrictToInternal;
+ };
};
+
+ networking.firewall.extraInputRules = ''
+ ip saddr ${config.os.core.network.ips.monitor-vm} tcp dport 9959 accept
+ '';
};
}
diff --git a/os/srv/backup.nix b/os/srv/backup.nix
index dad9b66..3d1d583 100644
--- a/os/srv/backup.nix
+++ b/os/srv/backup.nix
@@ -1,32 +1,94 @@
-{ config, lib, ... }:
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
let
- cfg = config.os.srv.backup;
+ cfg = config.os.srv.replication;
+ pgLockScript = pkgs.writeScriptBin "pg-lock" ''
+ #!/bin/sh
+ microvm -s database-vm -- sudo -u postgres psql -c "SELECT pg_backup_start('sanoid_snap');"
+ '';
+
+ pgUnlockScript = pkgs.writeScriptBin "pg-unlock" ''
+ #!/bin/sh
+ microvm -s database-vm -- sudo -u postgres psql -c "SELECT pg_backup_stop();"
+ '';
in
{
- options.os.srv.backup.enable = lib.mkEnableOption "enables backups";
+ options.os.srv.replication.enable = lib.mkEnableOption "enables replications";
config = lib.mkIf cfg.enable {
- services.sanoid = {
- enable = true;
- templates.production = {
- hourly = 36;
- daily = 30;
- monthly = 3;
- };
- datasets."zroot/rpool/appdata/databases".useTemplate = [ "production" ];
- datasets."ztank/vault".useTemplates = [ "production" ];
- };
- services.syncoid = {
- enable = true;
- commands = {
- "sync-db" = {
- source = "zroot/rpool/appdata/databases";
- target = "ztank/backups/nvme/databases";
- sendOptions = "w";
+ services = {
+ sanoid = {
+ enable = true;
+ templates.production = {
+ autosnap = true;
+ autoprune = true;
+ hourly = 24;
+ daily = 7;
+ weekly = 4;
+ monthly = 3;
};
- "sync-configs" = {
- source = "zroot/rpool/appdata/configs";
- target = "ztank/backups/nvme/configs";
- sendOptions = "w";
+ datasets = {
+ "zroot/rpool/appdata/db/postgres" = {
+ useTemplate = [ "production" ];
+
+ pre_snapshot_script = "${pgLockScript}/bin/pg-lock";
+ post_snapshot_script = "${pgUnlockScript}/bin/pg-unlock";
+ no_inconsistent_snapshot = true;
+ force_post_snapshot_script = true;
+ script_timeout = 30;
+ };
+
+ "zroot/rpool/appdata/db/redis".useTemplate = [ "production" ];
+
+ "zroot/rpool/appdata/db/couchdb".useTemplate = [ "production" ];
+
+ "zroot/rpool/appdata/cfg".useTemplate = [ "production" ];
+
+ "zroot/rpool/appdata/games".useTemplate = [ "production" ];
+
+ "zroot/rpool/appdata/mail".useTemplate = [ "production" ];
+
+ "zroot/rpool/containers".useTemplate = [ "production" ];
+ };
+ };
+
+ syncoid = {
+ enable = true;
+ commonArgs = [
+ "-w"
+ "--delete-target-snapshots"
+ "--use-hold"
+ "--no-sync-snap"
+ ];
+ commands = {
+ "sync-databases" = {
+ source = "zroot/rpool/appdata/db";
+ target = "tank/ztank/backup/nvme/db";
+ recursive = true;
+ };
+
+ "sync-config" = {
+ source = "zroot/rpool/appdata/cfg";
+ target = "tank/ztank/backup/nvme/cfg";
+ };
+
+ "sync-games" = {
+ source = "zroot/rpool/appdata/games";
+ target = "tank/ztank/backup/nvme/games";
+ };
+
+ "sync-mail" = {
+ source = "zroot/rpool/appdata/mail";
+ target = "tank/ztank/backup/nvme/mail";
+ };
+
+ "sync-docker" = {
+ source = "zroot/rpool/containers";
+ target = "tank/ztank/backup/nvme/containers";
+ };
};
};
};
diff --git a/os/srv/cluster.nix b/os/srv/cluster.nix
new file mode 100644
index 0000000..6e54ee2
--- /dev/null
+++ b/os/srv/cluster.nix
@@ -0,0 +1,16 @@
+{ lib, ... }: {
+ options.os.cluster = {
+ nginxProxies = lib.mkOption {
+ type = lib.types.attrsOf lib.types.attrs;
+ default = { };
+ };
+ autheliaRules = lib.mkOption {
+ type = lib.types.listOf lib.types.attrs;
+ default = [ ];
+ };
+ oidcClients = lib.mkOption {
+ type = lib.types.listOf lib.types.attrs;
+ default = [ ];
+ };
+ };
+}
diff --git a/os/srv/crowdsec.nix b/os/srv/crowdsec.nix
index 79c8718..71818bd 100644
--- a/os/srv/crowdsec.nix
+++ b/os/srv/crowdsec.nix
@@ -5,6 +5,9 @@ in
{
options.os.srv.security.crowdsec = {
enable = lib.mkEnableOption "enables CrowdSec collaborative intrusion prevention";
+
+ aggregator.enable = lib.mkEnableOption "this node acting as a central LAPI aggregator for the network";
+ agent.enable = lib.mkEnableOption "local log parsing and threat intelligence generation on this node";
};
config = lib.mkIf cfg.enable {
@@ -13,66 +16,104 @@ in
assertion = config.networking.nftables.enable;
message = "CrowdSec requires networking.nftables to be enabled for blocking.";
}
+ {
+ assertion = cfg.agent.enable || cfg.aggregator.enable;
+ message = "You must enable at least one CrowdSec role: 'agent.enable' or 'aggregator.enable'.";
+ }
];
services.crowdsec = {
enable = true;
autoUpdateService = true;
+ openFirewall = cfg.aggregator.enable;
+
+ settings = {
+ api.server.enable = cfg.aggregator.enable;
+ lapi.client.api_url = "http://${config.os.core.network.ips.gateway-vm}:8080";
+ };
+
+ hub = lib.mkIf cfg.agent.enable {
+ collections = [
+ "crowdsecurity/linux"
+ "crowdsecurity/nginx"
+ "crowdsecurity/authelia"
+ "crowdsecurity/sshd"
+ ];
+ };
+
localConfig = {
- acquisitions = [
+ acquisitions = lib.mkIf cfg.agent.enable [
{
source = "journalctl";
journalctl_filter = [ "_SYSTEMD_UNIT=sshd.service" ];
labels.type = "syslog";
}
{
- filenames = [
- "/var/log/nginx/access.log"
- "/var/log/nginx/error.log"
- ];
+ source = "file";
+ filenames = [ "/var/log/nginx/*.log" ];
labels.type = "nginx";
}
+ {
+ source = "file";
+ filenames = [ "/var/log/authelia/authelia.log" ];
+ labels.type = "authelia";
+ }
];
- parsers.s02Enrich = [
+ parsers.s02Enrich = lib.mkIf cfg.agent.enable [
{
name = "myips/whitelist";
description = "Prevent local address ranges from triggering bans";
whitelist = {
reason = "Internal private subnets";
cidr = [
- "10.0.0.0/16"
+ "10.0.0.0/24"
+ "10.1.0.0/24"
+ "10.3.0.0/24"
+ "10.4.0.0/24"
];
};
}
];
- };
- hub = {
- collections = [
- "crowdsecurity/linux"
- "crowdsecurity/nginx"
- "crowdsecurity/sshd"
+ notifications = lib.mkIf cfg.aggregator.enable [
+ {
+ name = "ntfy_alerts";
+ type = "http";
+ method = "POST";
+ #TODO add ntfy sops thing
+ url = "https://ntfy.sh/your_secret_topic_here";
+ headers = {
+ Title = "CrowdSec Alert on Bibus-Lab";
+ Priority = "high";
+ };
+ format = ''
+ {{range .}} {{.Alert.Message}} (Scenario: {{.Alert.Scenario}}) from IP {{.Alert.Source.IP}} {{end}}
+ '';
+ log_level = "info";
+ }
];
};
-
- settings = {
- lapi.credentialsFile = "/var/lib/crowdsec/state/lapi.yaml";
- capi.credentialsFile = "/var/lib/crowdsec/state/capi.yaml";
- };
};
services.crowdsec-firewall-bouncer = {
enable = true;
+
+ registerBouncer.enable = cfg.aggregator.enable;
+
settings = {
+ mode = "nftables";
update_frequency = "10s";
+
+ api_url = "http://${config.os.core.network.ips.gateway-vm}:8080";
};
};
- users.users.crowdsec.extraGroups = [
- "nginx"
+ users.users.crowdsec.extraGroups = lib.mkIf cfg.agent.enable [
"systemd-journal"
+ "nginx"
+ "authelia-main"
];
};
}
diff --git a/os/srv/default.nix b/os/srv/default.nix
index 34aa179..073067d 100644
--- a/os/srv/default.nix
+++ b/os/srv/default.nix
@@ -5,6 +5,7 @@
./backup.nix
./bluetooth.nix
./clamav.nix
+ ./cluster.nix
./compat.nix
./crowdsec.nix
./dns.nix
@@ -26,7 +27,9 @@
./ntopng.nix
./oci.nix
./omnisearch.nix
+ ./postgres.nix
./prometheus.nix
+ ./redis.nix
./scrutiny.nix
./simplex.nix
./sops.nix
@@ -37,6 +40,7 @@
./tor.nix
./ups.nix
./uptime-kuma.nix
+ ./vector.nix
./virtualization.nix
./wireguard.nix
./yggdrasil.nix
diff --git a/os/srv/gaming.nix b/os/srv/gaming.nix
index 36f2db5..730dcd2 100644
--- a/os/srv/gaming.nix
+++ b/os/srv/gaming.nix
@@ -30,22 +30,24 @@ in
config = lib.mkMerge [
# --- PERFORMANCE & TOOLING ---
(lib.mkIf cfg.tools.enable {
- programs.gamescope = {
- enable = true;
- capSysNice = true;
- };
+ hardware.xone.enable = true;
+ programs = {
+ gamescope = {
+ enable = true;
+ capSysNice = true;
+ };
- programs.gamemode = {
- enable = true;
- enableRenice = true;
- settings = {
- general.renice = 10;
+ gamemode = {
+ enable = true;
+ enableRenice = true;
+ settings = {
+ general.renice = 10;
+ };
};
};
environment = {
- # sets the optiscaler shortcut key to be home by default
sessionVariables = {
- OPTISCALER_ShortcutKey = "0x24";
+ OPTISCALER_ShortcutKey = "0x24"; # sets the optiscaler shortcut key to be home by default
};
systemPackages = with pkgs; [
@@ -61,10 +63,9 @@ in
(lib.mkIf cfg.launchers.enable {
environment.systemPackages = with pkgs; [
heroic
- # (pkgs.bottles.override { removeWarningPopup = true; })
(prismlauncher.override {
additionalLibs = with pkgs; [ ocl-icd ];
- jdks = with pkgs; [ javaPackages.compiler.temurin-bin.jdk-25 ];
+ jdks = with pkgs; [ javaPackages.compiler.temurin-bin.jdk-26 ];
})
];
})
diff --git a/os/srv/headscale.nix b/os/srv/headscale.nix
index a650067..01fc06c 100644
--- a/os/srv/headscale.nix
+++ b/os/srv/headscale.nix
@@ -1,14 +1,46 @@
{
config,
lib,
+ pkgs,
masterDomain,
...
}:
let
cfg = config.os.srv.headscale;
+ aclPolicy = pkgs.writeText "headscale-policy.json" (
+ builtins.toJSON {
+ groups = {
+ "group:admin" = [ "your-device-name" ];
+ "group:friends" = [ "friend-device-name" ];
+ };
+
+ hosts = {
+ "server" = "10.4.0.1";
+ };
+
+ acls = [
+ {
+ action = "accept";
+ src = [ "group:admin" ];
+ dst = [ "*:*" ];
+ }
+
+ {
+ action = "accept";
+ src = [ "group:friends" ];
+ dst = [
+ "server:18080"
+ "server:18081"
+ "server:25565"
+ ];
+ }
+ ];
+ }
+ );
in
{
options.os.srv.headscale.enable = lib.mkEnableOption "enables headscales";
+
config = lib.mkIf cfg.enable {
services.headscale = {
enable = true;
@@ -18,14 +50,16 @@ in
settings = {
server_url = "https://vpn.${masterDomain}";
+ policy.path = "${aclPolicy}";
+
dns = {
magic_dns = true;
- base_domain = "vpn.internal";
- nameservers = [ "10.255.1.1" ];
+ base_domain = "vpn";
+ nameservers = [ config.os.core.network.ips.vm2-gateway ];
};
ip_prefixes = [
- "10.254.0.0/16"
+ "10.4.0.0/16"
];
};
};
diff --git a/os/srv/i2p.nix b/os/srv/i2p.nix
index fa7f102..5e36c20 100644
--- a/os/srv/i2p.nix
+++ b/os/srv/i2p.nix
@@ -1,7 +1,8 @@
{
config,
lib,
- pkgs,
+ masterDomain,
+ securityTemplates,
...
}:
let
@@ -9,45 +10,72 @@ let
in
{
options.os.srv.i2p = {
- enable = lib.mkEnableOption "enables i2pd";
- enableBrowser = lib.mkEnableOption "enables mullvad browser for browsing eepsites";
+ enable = lib.mkEnableOption "enables a flexible, polymorphic i2pd deployment profile";
+
+ mode = lib.mkOption {
+ type = lib.types.enum [
+ "server"
+ "client"
+ ];
+ default = "client";
+ description = "";
+ };
};
- config = lib.mkMerge [
- (lib.mkIf cfg.enable {
- services.i2pd = {
- enable = true;
- enableIPv6 = true;
- # upnp.enable = true;
- bandwidth = 1024;
- reseed.verify = true;
- ntcp2 = {
- enable = true;
- # published = true;
- };
- ssu2 = {
+ config = lib.mkIf cfg.enable (
+ lib.mkMerge [
+ {
+ services.i2pd = {
enable = true;
- # published = true;
+ enableIPv6 = true;
+ reseed.verify = true;
+
+ yggdrasil.enable = true;
+
+ proto = {
+ http.enable = true;
+ httpProxy.enable = true;
+ socksProxy = {
+ enable = true;
+ outproxyEnable = true;
+ };
+ sam.enable = true;
+ i2pControl.enable = true;
+ };
};
- yggdrasil = {
- enable = true;
- address = "200:5857:a255:4db6:8687:6928:ddc4:dad6";
+ }
+
+ (lib.mkIf (cfg.mode == "server") {
+ services.i2pd = {
+ bandwidth = 4096;
+
+ ntcp2.published = true;
+ ssu2.published = true;
+
+ #TODO add address
+ yggdrasil.address = "";
};
- proto = {
- http.enable = true;
- httpProxy.enable = true;
- socksProxy = {
- enable = true;
- outproxyEnable = true;
+
+ os.cluster.nginxProxies."i2p.${masterDomain}" = {
+ enableACME = true;
+ forceSSL = true;
+ locations."/" = {
+ proxyPass = "http://${config.os.core.network.ips.relay-vm}:7070";
+ extraConfig = securityTemplates.restrictToInternal;
};
- sam.enable = true;
- i2pControl.enable = true;
};
- };
- # environment.systemPackages = [ pkgs.i2pd-tools ];
- })
- (lib.mkIf cfg.enableBrowser {
- environment.systemPackages = [ pkgs.mullvad-browser ];
- })
- ];
+ })
+
+ (lib.mkIf (cfg.mode == "client") {
+ services.i2pd = {
+ bandwidth = 512;
+
+ ntcp2.published = false;
+ ssu2.published = false;
+
+ yggdrasil.address = "";
+ };
+ })
+ ]
+ );
}
diff --git a/os/srv/lldap.nix b/os/srv/lldap.nix
index 1cf43e4..1463ab6 100644
--- a/os/srv/lldap.nix
+++ b/os/srv/lldap.nix
@@ -1,6 +1,15 @@
-{ config, lib, ... }:
+{
+ config,
+ lib,
+ masterDomain,
+ securityTemplates,
+ ...
+}:
let
cfg = config.os.srv.lldap;
+ computedBaseDN = lib.concatStringsSep "," (
+ map (domainPart: "dc=${domainPart}") (lib.splitString "." masterDomain)
+ );
in
{
options.os.srv.lldap.enable = lib.mkEnableOption "enables lldap scanning";
@@ -10,15 +19,48 @@ in
assertion = config.os.srv.sops.enable;
message = "Required for password secure password storing";
}
+ {
+ assertion = config.os.core.network.enableFirewall;
+ message = "Requires firewall";
+ }
];
+
+ sops.secrets = {
+ "lldap/password" = {
+ owner = "lldap";
+ group = "lldap";
+ };
+ "lldap/env_file" = {
+ owner = "lldap";
+ group = "lldap";
+ };
+ };
+
services.lldap = {
enable = true;
settings = {
- ldap_base_dn = "dc=example,dc=com";
- ldap_port = 3890;
- http_port = 17170;
+ ldap_base_dn = computedBaseDN;
+ http_host = "127.0.0.1";
+ http_url = "https://lldap.${masterDomain}";
+ ldap_user_email = "adikro@disroot.org";
+ ldap_user_pass_file = config.sops.secrets."lldap/password".path;
+ silenceForceUserPassResetWarning = true;
+ };
+ environmentFile = config.sops.secrets."lldap/env_file".path;
+ };
+
+ os.cluster.nginxProxies."lldap.${masterDomain}" = {
+ enableACME = true;
+ forceSSL = true;
+
+ locations."/" = {
+ proxyPass = "http://${config.os.core.network.ips.gateway-vm}:17170";
+ extraConfig = securityTemplates.restrictToInternal;
};
- environmentFile = config.sops.secrets."lldap/env".path;
};
+
+ networking.firewall.extraInputRules = ''
+ ip saddr 10.0.0.0/24 tcp dport 3890 accept
+ '';
};
}
diff --git a/os/srv/monero.nix b/os/srv/monero.nix
index f00413d..6b50e1d 100644
--- a/os/srv/monero.nix
+++ b/os/srv/monero.nix
@@ -8,16 +8,28 @@
}:
let
cfg = config.os.srv.monero;
+ banlist1 = pkgs.fetchurl {
+ url = "https://gui.xmr.pm/files/block.txt";
+ hash = "sha256-0ik4d66js6wvrvciza0li6bsajj8dvxsqlf09hcz7hg610szdxcw";
+ };
+ banlist2 = pkgs.fetchurl {
+ url = "https://raw.githubusercontent.com/Boog900/monero-ban-list/refs/heads/main/ban_list.txt";
+ hash = "sh256-01z4wm2mp4z1wq2wdkrm66j50gwk3r82m2ml4n0pwjcbajxkdc87";
+ };
+
+ combinedBanlist = pkgs.writeText "combined-monero-banlist.txt" ''
+ ${builtins.readFile banlist1}
+ ${builtins.readFile banlist2}
+ '';
in
{
options.os.srv.monero = {
wallet.enable = lib.mkEnableOption "enables the monero wallet";
service = {
enable = lib.mkEnableOption "enables hosting a monero node";
- proxyConfig = lib.mkOption {
- type = lib.types.attrs;
- default = { };
- };
+ public = lib.mkEnableOption "makes the RPC node public (disables authentication for general wallet syncing)";
+ tor.enable = lib.mkEnableOption "exposes monero RPC via Tor Onion Service";
+ i2p.enable = lib.mkEnableOption "exposes monero RPC via I2P Tunnel";
};
};
@@ -28,8 +40,16 @@ in
(lib.mkIf cfg.service.enable {
assertions = [
{
- assertion = config.os.srv.sops.enable;
- message = "Required for password secure password storing";
+ assertion = if (!cfg.service.public) then config.os.srv.sops.enable else true;
+ message = "sops must be enabled";
+ }
+ {
+ assertion = if cfg.service.tor.enable then config.os.srv.tor.enable else true;
+ message = "tor must be enabled";
+ }
+ {
+ assertion = if cfg.service.i2p.enable then config.os.srv.i2p.enable else true;
+ message = "i2p must be enabled";
}
];
@@ -41,37 +61,59 @@ in
services.monero = {
enable = true;
prune = true;
+ banlist = combinedBanlist;
+
limits = {
upload = 1250;
download = 12500;
threads = 8;
};
- banlist = builtins.fetchurl {
- url = "https://gui.xmr.pm/files/block.txt";
- hash = "0ik4d66js6wvrvciza0li6bsajj8dvxsqlf09hcz7hg610szdxcw";
- };
+
rpc = {
+ address = "0.0.0.0";
+ }
+ // lib.optionalAttrs (!cfg.service.public) {
restricted = true;
user = "admin";
password = config.sops.secrets."monero/rpc-password".path;
};
+
+ };
+
+ services.tor = lib.mkIf cfg.service.tor.enable {
+ onionServices."xmr-rpc" = {
+ to = [
+ {
+ port = 18081;
+ address = config.os.core.network.ips.vm9-relays;
+ }
+ ];
+ };
+ };
+
+ services.i2pd = lib.mkIf cfg.service.i2p.enable {
+ tunnels.server."xmr-rpc" = {
+ port = 18081;
+ address = config.os.core.network.ips.vm9-relays;
+ keys = "xmr-rpc-key.dat";
+ inbound.length = 3;
+ outbound.length = 3;
+ };
};
- os.srv.monero.service.proxyConfig = {
- "xmr.${masterDomain}" = {
- enableACME = true;
- forceSSL = true;
+ os.cluster.nginxProxies."xmr.${masterDomain}" = {
+ enableACME = true;
+ forceSSL = true;
- locations."/" = {
- proxyPass = "http://${config.os.core.network.ips.vm9-relays}:18081";
- extraConfig = ''
- proxy_read_timeout 600s;
- proxy_send_timeout 600s;
- client_max_body_size 50m;
+ locations."/" = {
+ proxyPass = "http://${config.os.core.network.ips.vm9-relays}:18081";
+ extraConfig = ''
+ proxy_read_timeout 600s;
+ proxy_send_timeout 600s;
+ client_max_body_size 50m;
- ${securityTemplates.restrictToInternal}
- '';
- };
+ ${securityTemplates.restrictToInternal}
+ '';
};
};
diff --git a/os/srv/nfs.nix b/os/srv/nfs.nix
index 9e5b16f..07f331e 100644
--- a/os/srv/nfs.nix
+++ b/os/srv/nfs.nix
@@ -7,14 +7,17 @@ in
config = lib.mkIf cfg.enable {
services.nfs.server = {
enable = true;
+ nproc = 4; # Lowered due to low traffic for a home server
+ createMountPoints = true;
- # TODO exports
exports = ''
- /data 192.168.1.0/24(ro,fsid=0,no_subtree_check)
+ /data/media 10.1.0.0/24(rw,all_squash,anonuid=1000,anongid=100,async,insecure,no_subtree_check) \
+ 10.3.0.0/24(rw,all_squash,anonuid=1000,anongid=100,async,insecure,no_subtree_check) \
+ 10.4.0.0/24(rw,all_squash,anonuid=1000,anongid=100,async,insecure,no_subtree_check)
- /data/media 192.168.1.0/24(ro,nohide,insecure,no_subtree_check,async)
-
- /data/backups 192.168.1.50(rw,nohide,no_subtree_check,sync,no_root_squash)
+ /data/vault 10.1.0.0/24(rw,all_squash,anonuid=1000,anongid=100,async,insecure,no_subtree_check) \
+ 10.3.0.0/24(rw,all_squash,anonuid=1000,anongid=100,async,insecure,no_subtree_check) \
+ 10.4.0.0/24(rw,all_squash,anonuid=1000,anongid=100,async,insecure,no_subtree_check)
'';
};
diff --git a/os/srv/nginx.nix b/os/srv/nginx.nix
index 5161920..2194ecb 100644
--- a/os/srv/nginx.nix
+++ b/os/srv/nginx.nix
@@ -51,7 +51,7 @@ in
locations."/".return = "444";
};
}
- config.os.srv.monero.proxyConfig
+ config.os.cluster.nginxProxies
];
};
diff --git a/os/srv/postgres.nix b/os/srv/postgres.nix
new file mode 100644
index 0000000..f669f63
--- /dev/null
+++ b/os/srv/postgres.nix
@@ -0,0 +1,72 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+let
+ cfg = config.os.srv.postgres;
+in
+{
+ options.os.srv.postgres.enable = lib.mkEnableOption "";
+ config = lib.mkIf cfg.enable {
+ assertions = [
+ {
+ assertion = config.os.srv.sops.enable;
+ message = "Required for password secure password storing";
+ }
+ {
+ assertion = config.os.core.network.enableFirewall;
+ message = "Requires firewall";
+ }
+ ];
+
+ sops.secrets."postgres/authelia_password" = {
+ owner = "postgres";
+ group = "postgres";
+ restartUnits = [ "postgresql.service" ];
+ };
+
+ services.postgresql = {
+ enable = true;
+ package = pkgs.postgresql_18;
+
+ extraPlugins = [ config.services.postgresql.package.pkgs.pgvector ];
+
+ settings = {
+ listen_addresses = config.os.core.network.ips.database-vm;
+
+ max_connections = 100;
+ shared_buffers = "256MB";
+ work_mem = "4MB";
+ };
+
+ ensureDatabases = [ "authelia" ];
+ ensureUsers = [
+ {
+ name = "authelia";
+ ensureDBOwnership = true;
+ }
+ ];
+
+ initialScript = pkgs.writeText "init-postgres-passwords.sql" ''
+ CREATE USER authelia;
+ ALTER USER authelia WITH PASSWORD 'scram-sha-256';
+ '';
+
+ authentication = pkgs.lib.mkForce ''
+ local all all trust
+ host all all 10.0.0.0/24 scram-sha-256
+ '';
+ };
+
+ systemd.services.postgresql.postStart = lib.mkAfter ''
+ PASS=$(cat ${config.sops.secrets."postgres/authelia_password".path})
+ ${config.services.postgresql.package}/bin/psql -tAc "ALTER USER authelia WITH PASSWORD '$PASS';"
+ '';
+
+ networking.firewall.extraInputRules = ''
+ ip saddr 10.0.0.0/24 tcp dport 5432 accept
+ '';
+ };
+}
diff --git a/os/srv/redis.nix b/os/srv/redis.nix
new file mode 100644
index 0000000..a51f9db
--- /dev/null
+++ b/os/srv/redis.nix
@@ -0,0 +1,37 @@
+{ config, lib, ... }:
+let
+ cfg = config.os.srv.redis;
+in
+{
+ options.os.srv.redis.enable = lib.mkEnableOption "";
+ config = lib.mkIf cfg.enable {
+ assertions = [
+ {
+ assertion = config.os.srv.sops.enable;
+ message = "Required for password secure password storing";
+ }
+ {
+ assertion = config.os.core.network.enableFirewall;
+ message = "Requires firewall";
+ }
+ ];
+
+ sops.secrets."redis/password" = {
+ owner = "redis-main";
+ restartUnits = [ "redis-servers-main.service" ];
+ };
+
+ services.redis.servers."main" = {
+ enable = true;
+ bind = config.os.core.network.ips.database-vm;
+ port = 6379;
+
+ requirePassFile = config.sops.secrets."redis/password".path;
+ };
+
+ networking.firewall.extraInputRules = ''
+ ip saddr 10.0.0.0/24 tcp dport 6379 accept
+ '';
+
+ };
+}
diff --git a/os/srv/restic.nix b/os/srv/restic.nix
new file mode 100644
index 0000000..fb2bd19
--- /dev/null
+++ b/os/srv/restic.nix
@@ -0,0 +1,12 @@
+{ config, lib, ... }:
+let
+ cfg = config.os.srv.restic;
+in
+{
+ options.os.srv.restic.enable = lib.mkEnableOption "enables restic backups";
+
+ config = lib.mkIf cfg.enable {
+ assertions = [
+ ];
+ };
+}
diff --git a/os/srv/wireguard.nix b/os/srv/wireguard.nix
index 363ac9f..c758174 100644
--- a/os/srv/wireguard.nix
+++ b/os/srv/wireguard.nix
@@ -107,13 +107,13 @@ in
};
networking.wireguard.interfaces.wg0 = {
- ips = [ "10.255.1.1/24" ];
+ ips = [ "10.3.0.1/24" ];
listenPort = 51280;
privateKeyFile = config.sops.secrets."wg_private_key/${hostname}".path;
peers = lib.imap1 (i: peer: {
publicKey = peer.publicKey;
- allowedIPs = [ "10.255.0.${toString i}/32" ];
+ allowedIPs = [ "10.3.0.${toString (i + 1)}/32" ];
persistentKeepalive = 25;
}) cfg.server.peers;
};
@@ -128,12 +128,12 @@ in
];
networking.nameservers = [
- "10.255.1.1"
+ config.os.core.network.ips.vm2-gateway
"9.9.9.9"
];
networking.wireguard.interfaces.wg0 = {
- ips = [ "10.255.0.${toString cfg.client.index}/24" ];
+ ips = [ "10.3.0.${toString cfg.client.index + 1}/24" ];
privateKeyFile = config.sops.secrets."wg_private_key/${hostname}".path;
peers = [
diff --git a/os/srv/zfs.nix b/os/srv/zfs.nix
index fadfd82..3bfd2de 100644
--- a/os/srv/zfs.nix
+++ b/os/srv/zfs.nix
@@ -10,43 +10,51 @@ in
assertion = config.os.core.drivers.kernel == "zfs";
message = "ZFS requires the zfs supported kernel";
}
+ {
+ assertion = config.os.srv.sops.enable;
+ message = "required for storing the ntfy token";
+ }
];
+
+ sops.secrets."ntfy/zed".neededForUsers = false;
+
boot = {
- kernelParams = [ "zfs.zfs_arc_max=34359738368" ];
- supportedFilesystems = [ "zfs" ];
- initrd = {
- supportedFilesystems = [ "zfs" ];
- # fileSystems."/mnt" = {
- # device = "/dev/disk/by-label/KEYS";
- # fsType = "vfat";
- # options = [ "ro" ];
- # };
+ kernelParams = [ "zfs.zfs_arc_max=${toString (32 * 1024 * 1024 * 1024)}" ];
+ zfs = {
+ requestEncryptionCredentials = [ "zroot" ];
+ useKeyringForCredentials = true;
+ extraPools = [ "tank" ];
};
+ supportedFilesystems = [ "zfs" ];
+ initrd.supportedFilesystems = [ "zfs" ];
};
services.zfs = {
- autoScrub = {
+ expandOnBoot = "all";
+ autoScrub.enable = true;
+ trim.enable = true;
+ autoSnapshot = {
enable = true;
- interval = "weekly";
+ flags = "-k -p --utc";
};
- trim.enable = true;
zed = {
- enableMail = true;
+ enableCustomScripts = true;
settings = {
ZED_DEBUG_LOG = "/var/log/zed.debug.log";
- ZED_EMAIL_ADDR = [ "adikro@disroot.org" ];
- ZED_EMAIL_PROG = "mail";
- ZED_EMAIL_OPTS = "-s '@SUBJECT@' @ADDRESS@";
-
ZED_NOTIFY_INTERVAL_SECS = 3600;
- ZED_NOTIFY_VERBOSE = false;
+ ZED_NOTIFY_VERBOSE = 0;
- ZED_USE_ENCLOSURE_LEDS = true;
- ZED_SCRUB_AFTER_RESILVER = false;
+ ZED_USE_ENCLOSURE_LEDS = 1;
+ ZED_SCRUB_AFTER_RESILVER = 1;
+ ZED_POWER_OFF_ENCLOSURE_SLOT_ON_FAULT = 1;
+ ZED_POWER_OFF_ENCLOSURE_SLOT_ON_DEADMAN = 1;
+ ZED_NTFY_TOPIC = "zed-alerts-bibus-lab";
+ ZED_NTFY_URL = "http://${config.os.core.network.ips.monitor-vm}:8085";
};
};
};
+ systemd.services.zfs-zed.serviceConfig.EnvironmentFile = config.sops.secrets."ntfy/zed".path;
networking.hostId = "4e3e22e1";
};
}