From 9b8f9a4bf5e13efe49ec101f127d0df3d7bb3cf3 Mon Sep 17 00:00:00 2001 From: adikro Date: Sat, 27 Jun 2026 02:41:27 +0200 Subject: revamped sanoid, syncoid and nfs --- os/srv/authelia.nix | 152 +++++++++++++++++++++++++++++++++++++++++++++------ os/srv/backup.nix | 110 +++++++++++++++++++++++++++++-------- os/srv/cluster.nix | 16 ++++++ os/srv/crowdsec.nix | 81 ++++++++++++++++++++------- os/srv/default.nix | 4 ++ os/srv/gaming.nix | 27 ++++----- os/srv/headscale.nix | 40 +++++++++++++- os/srv/i2p.nix | 98 +++++++++++++++++++++------------ os/srv/lldap.nix | 52 ++++++++++++++++-- os/srv/monero.nix | 88 +++++++++++++++++++++-------- os/srv/nfs.nix | 13 +++-- os/srv/nginx.nix | 2 +- os/srv/postgres.nix | 72 ++++++++++++++++++++++++ os/srv/redis.nix | 37 +++++++++++++ os/srv/restic.nix | 12 ++++ os/srv/wireguard.nix | 8 +-- os/srv/zfs.nix | 48 +++++++++------- 17 files changed, 690 insertions(+), 170 deletions(-) create mode 100644 os/srv/cluster.nix create mode 100644 os/srv/postgres.nix create mode 100644 os/srv/redis.nix create mode 100644 os/srv/restic.nix (limited to 'os/srv') 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"; }; } -- cgit v1.3