From 8f44273faf2b16edae4c6b8eba8f2b1772544553 Mon Sep 17 00:00:00 2001 From: ashisgreat22 Date: Wed, 18 Mar 2026 21:33:42 +0100 Subject: [PATCH 01/10] Cleanup --- modules/adguard.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/adguard.nix b/modules/adguard.nix index 94caadc..2ac325e 100644 --- a/modules/adguard.nix +++ b/modules/adguard.nix @@ -56,7 +56,7 @@ in port = cfg.port; settings = { dns = { - bind_hosts = [ "127.0.0.1" ]; + bind_hosts = [ "0.0.0.0" ]; port = 5353; upstream_dns = [ cfg.upstreamDoh ]; bootstrap_dns = cfg.bootstrapDns; From c3adfa7e25882c7d883f1574bcedc62cfc0ca0b1 Mon Sep 17 00:00:00 2001 From: ashisgreat22 Date: Wed, 18 Mar 2026 21:53:05 +0100 Subject: [PATCH 02/10] Restrict incoming connections to DE via CrowdSec GeoIP --- modules/crowdsec.nix | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/modules/crowdsec.nix b/modules/crowdsec.nix index 2d6f1fa..4b15ce9 100644 --- a/modules/crowdsec.nix +++ b/modules/crowdsec.nix @@ -91,6 +91,17 @@ in # Remediation profiles localConfig.profiles = [ + { + name = "block_non_de"; + filters = [ "Alert.Remediation == true && Alert.GetScope() == 'Ip' && Alert.Enriched.IsoCode != 'DE' && Alert.Enriched.IsoCode != ''" ]; + decisions = [ + { + type = "ban"; + duration = "24h"; + } + ]; + on_success = "break"; + } { name = "default_ip_remediation"; filters = [ "Alert.Remediation == true && Alert.GetScope() == 'Ip'" ]; @@ -106,6 +117,9 @@ in # Hub collections for common attack patterns hub = { + parsers = [ + "crowdsecurity/geoip-enrich" + ]; collections = [ "crowdsecurity/linux" "crowdsecurity/nginx" From 4790078ff9bfab3fa2ca9133d8c8cb9aeb81e65f Mon Sep 17 00:00:00 2001 From: ashisgreat22 Date: Wed, 18 Mar 2026 21:54:34 +0100 Subject: [PATCH 03/10] Fix CrowdSec GeoIP filter syntax --- modules/crowdsec.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/crowdsec.nix b/modules/crowdsec.nix index 4b15ce9..eeba0e8 100644 --- a/modules/crowdsec.nix +++ b/modules/crowdsec.nix @@ -93,7 +93,7 @@ in localConfig.profiles = [ { name = "block_non_de"; - filters = [ "Alert.Remediation == true && Alert.GetScope() == 'Ip' && Alert.Enriched.IsoCode != 'DE' && Alert.Enriched.IsoCode != ''" ]; + filters = [ "Alert.Remediation == true && Alert.GetScope() == 'Ip' && Alert.Source.Cn != 'DE' && Alert.Source.Cn != ''" ]; decisions = [ { type = "ban"; From 7ea9246d74c9b5d387c2772cda2e902c9084f821 Mon Sep 17 00:00:00 2001 From: ashisgreat22 Date: Wed, 18 Mar 2026 22:01:38 +0100 Subject: [PATCH 04/10] Manage AdGuard Home blocklists via NixOS using yq-go injection --- modules/adguard.nix | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/modules/adguard.nix b/modules/adguard.nix index 2ac325e..87e0d0c 100644 --- a/modules/adguard.nix +++ b/modules/adguard.nix @@ -47,9 +47,42 @@ in default = [ "194.242.2.2" "2a07:e340::2" ]; description = "Bootstrap DNS servers for resolving DoH upstream"; }; + + filters = lib.mkOption { + type = lib.types.listOf (lib.types.submodule { + options = { + name = lib.mkOption { + type = lib.types.str; + description = "Friendly name for the filter list"; + }; + url = lib.mkOption { + type = lib.types.str; + description = "URL of the filter list (txt format)"; + }; + enabled = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Whether the filter is enabled"; + }; + }; + }); + default = [ + { name = "AdGuard DNS filter"; url = "https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt"; } + { name = "AdAway Default Blocklist"; url = "https://adguardteam.github.io/HostlistsRegistry/assets/filter_2.txt"; } + { name = "HaGeZi Multi Light"; url = "https://hagezi.github.io/dns-blocklists/wildcard/light.txt"; } + { name = "OISD Basic"; url = "https://small.oisd.nl/"; } + { name = "Peter Lowe's List"; url = "https://pgl.yoyo.org/adservers/serverlist.php?hostformat=adguard&showintro=0&mimetype=plaintext"; } + ]; + description = "DNS blocklists to maintain in AdGuard Home"; + }; }; config = lib.mkIf cfg.enable { + # Generate a temporary JSON file containing the filters for yq to inject + systemd.tmpfiles.rules = [ + "f /run/adguardhome_filters.json 0644 root root - ${builtins.toJSON { filters = map (f: { inherit (f) name url enabled; id = (builtins.hashString "sha256" f.url); }) cfg.filters; }}" + ]; + services.adguardhome = { enable = true; host = "127.0.0.1"; @@ -104,11 +137,17 @@ in }; }; - # Give AdGuardHome access to ACME certificates + # Inject filters into AdGuardHome.yaml before starting systemd.services.adguardhome = { requires = [ "acme-${cfg.domain}.service" ]; after = [ "acme-${cfg.domain}.service" ]; - serviceConfig.SupplementaryGroups = [ "acme" "nginx" ]; + serviceConfig.SupplementaryGroups = [ "acme" ]; + serviceConfig.SystemCallFilter = lib.mkForce []; # Allow yq-go to run its syscalls + preStart = lib.mkAfter '' + if [ -f /var/lib/private/AdGuardHome/AdGuardHome.yaml ]; then + ${pkgs.yq-go}/bin/yq -i '.filters = load("/run/adguardhome_filters.json").filters' /var/lib/private/AdGuardHome/AdGuardHome.yaml + fi + ''; }; # Open firewall for DoT From 8a9c513fde9a48c38f526ffe3148437204ff99d6 Mon Sep 17 00:00:00 2001 From: ashisgreat22 Date: Wed, 18 Mar 2026 22:02:44 +0100 Subject: [PATCH 05/10] Fix AdGuard filter ID type (string to integer) --- modules/adguard.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/adguard.nix b/modules/adguard.nix index 87e0d0c..75237ae 100644 --- a/modules/adguard.nix +++ b/modules/adguard.nix @@ -80,7 +80,7 @@ in config = lib.mkIf cfg.enable { # Generate a temporary JSON file containing the filters for yq to inject systemd.tmpfiles.rules = [ - "f /run/adguardhome_filters.json 0644 root root - ${builtins.toJSON { filters = map (f: { inherit (f) name url enabled; id = (builtins.hashString "sha256" f.url); }) cfg.filters; }}" + "f /run/adguardhome_filters.json 0644 root root - ${builtins.toJSON { filters = lib.imap0 (i: f: { inherit (f) name url enabled; id = i + 1; }) cfg.filters; }}" ]; services.adguardhome = { From 223f716b856cdd6ab16da83bd1b1ac3c6a5883bb Mon Sep 17 00:00:00 2001 From: ashisgreat22 Date: Wed, 18 Mar 2026 22:06:47 +0100 Subject: [PATCH 06/10] Remove explicit filter IDs from AdGuard config to avoid unmarshalling errors --- modules/adguard.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/adguard.nix b/modules/adguard.nix index 75237ae..147039e 100644 --- a/modules/adguard.nix +++ b/modules/adguard.nix @@ -80,7 +80,7 @@ in config = lib.mkIf cfg.enable { # Generate a temporary JSON file containing the filters for yq to inject systemd.tmpfiles.rules = [ - "f /run/adguardhome_filters.json 0644 root root - ${builtins.toJSON { filters = lib.imap0 (i: f: { inherit (f) name url enabled; id = i + 1; }) cfg.filters; }}" + "f /run/adguardhome_filters.json 0644 root root - ${builtins.toJSON { filters = map (f: { inherit (f) name url enabled; }) cfg.filters; }}" ]; services.adguardhome = { From deedd007624519240170578d812963d1a4e92f8a Mon Sep 17 00:00:00 2001 From: ashisgreat22 Date: Wed, 18 Mar 2026 22:11:08 +0100 Subject: [PATCH 07/10] Automate certificate path injection in AdGuard Home config --- modules/adguard.nix | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/adguard.nix b/modules/adguard.nix index 147039e..cd329f7 100644 --- a/modules/adguard.nix +++ b/modules/adguard.nix @@ -145,7 +145,11 @@ in serviceConfig.SystemCallFilter = lib.mkForce []; # Allow yq-go to run its syscalls preStart = lib.mkAfter '' if [ -f /var/lib/private/AdGuardHome/AdGuardHome.yaml ]; then - ${pkgs.yq-go}/bin/yq -i '.filters = load("/run/adguardhome_filters.json").filters' /var/lib/private/AdGuardHome/AdGuardHome.yaml + ${pkgs.yq-go}/bin/yq -i ' + .filters = load("/run/adguardhome_filters.json").filters | + .tls.certificate_path = "/var/lib/acme/${cfg.domain}/fullchain.pem" | + .tls.private_key_path = "/var/lib/acme/${cfg.domain}/key.pem" + ' /var/lib/private/AdGuardHome/AdGuardHome.yaml fi ''; }; From c51c7183c131e40a3b5f234a84294755e637ff29 Mon Sep 17 00:00:00 2001 From: ashisgreat22 Date: Wed, 18 Mar 2026 22:12:28 +0100 Subject: [PATCH 08/10] Allow AdGuard Home to read ACME certificates via ReadOnlyPaths --- modules/adguard.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/adguard.nix b/modules/adguard.nix index cd329f7..a4920a9 100644 --- a/modules/adguard.nix +++ b/modules/adguard.nix @@ -141,7 +141,8 @@ in systemd.services.adguardhome = { requires = [ "acme-${cfg.domain}.service" ]; after = [ "acme-${cfg.domain}.service" ]; - serviceConfig.SupplementaryGroups = [ "acme" ]; + serviceConfig.SupplementaryGroups = [ "acme" "nginx" ]; + serviceConfig.ReadOnlyPaths = [ "/var/lib/acme/${cfg.domain}" ]; serviceConfig.SystemCallFilter = lib.mkForce []; # Allow yq-go to run its syscalls preStart = lib.mkAfter '' if [ -f /var/lib/private/AdGuardHome/AdGuardHome.yaml ]; then From 6e9de4c189bf39aa46387b8d509c0b0145906903 Mon Sep 17 00:00:00 2001 From: ashisgreat22 Date: Wed, 18 Mar 2026 23:32:01 +0100 Subject: [PATCH 09/10] Add Forgejo self-hosted Git service with Nginx, PostgreSQL, and Restic backups --- configuration.nix | 7 ++++ modules/default.nix | 1 + modules/forgejo.nix | 85 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 modules/forgejo.nix diff --git a/configuration.nix b/configuration.nix index 525e76b..d1f881b 100644 --- a/configuration.nix +++ b/configuration.nix @@ -120,6 +120,13 @@ signupAllowed = false; }; + # === Forgejo (Self-hosted Git) === + myModules.forgejo = { + enable = true; + domain = "git.ashisgreat.xyz"; + disableRegistration = true; # Admin only + }; + # === CrowdSec === myModules.crowdsec.enable = true; diff --git a/modules/default.nix b/modules/default.nix index c40f93e..d889fe1 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -10,5 +10,6 @@ ./crowdsec.nix ./backup.nix ./adguard.nix + ./forgejo.nix ]; } diff --git a/modules/forgejo.nix b/modules/forgejo.nix new file mode 100644 index 0000000..c18813a --- /dev/null +++ b/modules/forgejo.nix @@ -0,0 +1,85 @@ +# Forgejo Module +# Provides: Self-hosted Git service (Fork of Gitea) +# +# Usage: +# myModules.forgejo = { +# enable = true; +# domain = "git.example.com"; +# }; + +{ + config, + lib, + pkgs, + ... +}: + +let + cfg = config.myModules.forgejo; +in +{ + options.myModules.forgejo = { + enable = lib.mkEnableOption "Forgejo Git service"; + + port = lib.mkOption { + type = lib.types.port; + default = 3002; + description = "Internal port to run Forgejo on"; + }; + + domain = lib.mkOption { + type = lib.types.str; + example = "git.example.com"; + description = "Public domain name for Forgejo"; + }; + + disableRegistration = lib.mkOption { + type = lib.types.bool; + default = true; + description = "Disable public user registration"; + }; + }; + + config = lib.mkIf cfg.enable { + services.forgejo = { + enable = true; + database.type = "postgres"; + + settings = { + server = { + DOMAIN = cfg.domain; + ROOT_URL = "https://${cfg.domain}/"; + HTTP_ADDR = "127.0.0.1"; + HTTP_PORT = cfg.port; + SSH_PORT = 2222; + }; + service = { + DISABLE_REGISTRATION = cfg.disableRegistration; + }; + session = { + COOKIE_SECURE = true; + }; + security = { + PASSWORD_COMPLEXITY = "lower,upper,digit,spec"; + MIN_PASSWORD_LENGTH = 12; + }; + }; + }; + + # Nginx Reverse Proxy + myModules.nginx.domains."${cfg.domain}" = { + port = cfg.port; + extraConfig = '' + client_max_body_size 512M; + ''; + }; + + # Open SSH port for Git + networking.firewall.allowedTCPPorts = [ 2222 ]; + + # Backups (Add Forgejo data to restic if backup module is enabled) + myModules.backup.paths = [ + config.services.forgejo.stateDir + ]; + }; +} From 748ccc6fc8752903e25937346c5ed23d6d459f8e Mon Sep 17 00:00:00 2001 From: ashisgreat22 Date: Wed, 18 Mar 2026 23:49:02 +0100 Subject: [PATCH 10/10] Enable Forgejo built-in SSH server on port 2222 --- modules/forgejo.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/forgejo.nix b/modules/forgejo.nix index c18813a..0a6e5cd 100644 --- a/modules/forgejo.nix +++ b/modules/forgejo.nix @@ -52,6 +52,8 @@ in HTTP_ADDR = "127.0.0.1"; HTTP_PORT = cfg.port; SSH_PORT = 2222; + START_SSH_SERVER = true; + SSH_LISTEN_ADDR = "0.0.0.0"; }; service = { DISABLE_REGISTRATION = cfg.disableRegistration;