193 lines
		
	
	
	
		
			6.6 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			193 lines
		
	
	
	
		
			6.6 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
{
 | 
						|
  config,
 | 
						|
  lib,
 | 
						|
  ...
 | 
						|
}: let
 | 
						|
  cfg = config.services.crab-hole;
 | 
						|
in {
 | 
						|
  options.services.crab-hole = {
 | 
						|
    port = lib.mkOption {
 | 
						|
      type = lib.types.port;
 | 
						|
      default = 8080;
 | 
						|
      description = "Port for the crab-hole API to listen on.";
 | 
						|
    };
 | 
						|
 | 
						|
    openFirewall = lib.mkOption {
 | 
						|
      type = lib.types.bool;
 | 
						|
      default = false;
 | 
						|
      description = "Whether to open the firewall for the crab-hole API port.";
 | 
						|
    };
 | 
						|
 | 
						|
    listen = lib.mkOption {
 | 
						|
      type = lib.types.str;
 | 
						|
      default = "0.0.0.0";
 | 
						|
      description = "Address for the crab-hole API to listen on.";
 | 
						|
    };
 | 
						|
 | 
						|
    show_doc = lib.mkEnableOption "OpenAPI documentation (loads content from third party websites)";
 | 
						|
 | 
						|
    downstreams = {
 | 
						|
      host = {
 | 
						|
        enable = lib.mkEnableOption "host downstream DNS server accessible from network on all interfaces";
 | 
						|
        port = lib.mkOption {
 | 
						|
          type = lib.types.port;
 | 
						|
          default = 53;
 | 
						|
          description = "Port for the host downstream DNS server to listen on.";
 | 
						|
        };
 | 
						|
        openFirewall = lib.mkEnableOption "automatic port forwarding for the host downstream";
 | 
						|
        disableSystemdResolved = lib.mkOption {
 | 
						|
          type = lib.types.bool;
 | 
						|
          default = true;
 | 
						|
          description = "Whether to automatically disable systemd-resolved when using port 53. Set to false if you want to handle the conflict manually.";
 | 
						|
        };
 | 
						|
      };
 | 
						|
    };
 | 
						|
 | 
						|
    extraDownstreams = lib.mkOption {
 | 
						|
      type = lib.types.listOf (lib.types.submodule {
 | 
						|
        options = {
 | 
						|
          protocol = lib.mkOption {
 | 
						|
            type = lib.types.enum ["udp" "tcp" "tls" "https" "quic"];
 | 
						|
            description = "Protocol for the downstream server.";
 | 
						|
          };
 | 
						|
 | 
						|
          listen = lib.mkOption {
 | 
						|
            type = lib.types.str;
 | 
						|
            description = "Address to listen on for downstream connections.";
 | 
						|
          };
 | 
						|
 | 
						|
          port = lib.mkOption {
 | 
						|
            type = lib.types.port;
 | 
						|
            description = "Port to listen on for downstream connections.";
 | 
						|
          };
 | 
						|
        };
 | 
						|
      });
 | 
						|
      default = [];
 | 
						|
      description = "List of additional downstream DNS server configurations.";
 | 
						|
    };
 | 
						|
 | 
						|
    upstreams = {
 | 
						|
      cloudFlare = {
 | 
						|
        enable = lib.mkEnableOption "Cloudflare DNS over TLS upstream servers (1.1.1.1 and 1.0.0.1)";
 | 
						|
      };
 | 
						|
    };
 | 
						|
 | 
						|
    extraUpstreams = lib.mkOption {
 | 
						|
      type = lib.types.listOf (lib.types.submodule {
 | 
						|
        options = {
 | 
						|
          socket_addr = lib.mkOption {
 | 
						|
            type = lib.types.str;
 | 
						|
            description = "Socket address of the upstream DNS server (e.g., \"1.1.1.1:853\" or \"[2606:4700:4700::1111]:853\").";
 | 
						|
          };
 | 
						|
 | 
						|
          protocol = lib.mkOption {
 | 
						|
            type = lib.types.enum ["udp" "tcp" "tls" "https" "quic"];
 | 
						|
            description = "Protocol to use for upstream DNS queries.";
 | 
						|
          };
 | 
						|
        };
 | 
						|
      });
 | 
						|
      default = [];
 | 
						|
      description = "List of additional upstream DNS server configurations.";
 | 
						|
    };
 | 
						|
 | 
						|
    blocklists = {
 | 
						|
      ad_malware = {
 | 
						|
        enable = lib.mkEnableOption "Host file for blocking ads and malware";
 | 
						|
        url = lib.mkOption {
 | 
						|
          type = lib.types.str;
 | 
						|
          default = "http://sbc.io/hosts/hosts";
 | 
						|
          description = "URL of the ad and malware blocklist host file";
 | 
						|
        };
 | 
						|
      };
 | 
						|
    };
 | 
						|
 | 
						|
    extraBlocklists = lib.mkOption {
 | 
						|
      type = lib.types.listOf lib.types.str;
 | 
						|
      default = [];
 | 
						|
      description = "Additional blocklist URLs to be added to the configuration";
 | 
						|
    };
 | 
						|
  };
 | 
						|
 | 
						|
  config = lib.mkIf cfg.enable {
 | 
						|
    # Assertions for proper configuration
 | 
						|
    assertions = [
 | 
						|
      {
 | 
						|
        assertion = !(cfg.downstreams.host.enable && cfg.downstreams.host.port == 53 && config.services.resolved.enable && cfg.downstreams.host.disableSystemdResolved);
 | 
						|
        message = "crab-hole host downstream cannot use port 53 while systemd-resolved is enabled. Either disable systemd-resolved or use a different port.";
 | 
						|
      }
 | 
						|
      {
 | 
						|
        assertion = !(cfg.downstreams.host.enable && cfg.downstreams.host.port == 53 && !cfg.downstreams.host.disableSystemdResolved && config.services.resolved.enable);
 | 
						|
        message = "crab-hole host downstream is configured to use port 53 but systemd-resolved is still enabled and disableSystemdResolved is false. Set disableSystemdResolved = true or manually disable systemd-resolved.";
 | 
						|
      }
 | 
						|
    ];
 | 
						|
 | 
						|
    # Automatically disable systemd-resolved if using port 53
 | 
						|
    services.resolved.enable = lib.mkIf (cfg.downstreams.host.enable && cfg.downstreams.host.port == 53 && cfg.downstreams.host.disableSystemdResolved) (lib.mkForce false);
 | 
						|
 | 
						|
    # Configure DNS nameservers when disabling systemd-resolved
 | 
						|
    networking.nameservers = lib.mkIf (cfg.downstreams.host.enable && cfg.downstreams.host.port == 53 && cfg.downstreams.host.disableSystemdResolved) (lib.mkDefault ["127.0.0.1" "1.1.1.1" "8.8.8.8"]);
 | 
						|
 | 
						|
    services.crab-hole.settings = lib.mkMerge [
 | 
						|
      {
 | 
						|
        api = {
 | 
						|
          port = cfg.port;
 | 
						|
          listen = cfg.listen;
 | 
						|
          show_doc = cfg.show_doc;
 | 
						|
        };
 | 
						|
        downstream = cfg.extraDownstreams;
 | 
						|
        upstream.name_servers = cfg.extraUpstreams;
 | 
						|
        blocklist.lists = cfg.extraBlocklists;
 | 
						|
      }
 | 
						|
      (lib.mkIf cfg.blocklists.ad_malware.enable {
 | 
						|
        blocklist.lists = [cfg.blocklists.ad_malware.url];
 | 
						|
      })
 | 
						|
      (lib.mkIf cfg.downstreams.host.enable {
 | 
						|
        downstream = [
 | 
						|
          {
 | 
						|
            protocol = "udp";
 | 
						|
            listen = "0.0.0.0";
 | 
						|
            port = cfg.downstreams.host.port;
 | 
						|
          }
 | 
						|
        ];
 | 
						|
      })
 | 
						|
      (lib.mkIf cfg.upstreams.cloudFlare.enable {
 | 
						|
        upstream.name_servers = [
 | 
						|
          {
 | 
						|
            socket_addr = "1.1.1.1:853";
 | 
						|
            protocol = "tls";
 | 
						|
            tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com";
 | 
						|
            trust_nx_responses = false;
 | 
						|
          }
 | 
						|
          {
 | 
						|
            socket_addr = "1.0.0.1:853";
 | 
						|
            protocol = "tls";
 | 
						|
            tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com";
 | 
						|
            trust_nx_responses = false;
 | 
						|
          }
 | 
						|
          {
 | 
						|
            socket_addr = "[2606:4700:4700::1111]:853";
 | 
						|
            protocol = "tls";
 | 
						|
            tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com";
 | 
						|
            trust_nx_responses = false;
 | 
						|
          }
 | 
						|
          {
 | 
						|
            socket_addr = "[2606:4700:4700::1001]:853";
 | 
						|
            protocol = "tls";
 | 
						|
            tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com";
 | 
						|
            trust_nx_responses = false;
 | 
						|
          }
 | 
						|
        ];
 | 
						|
      })
 | 
						|
    ];
 | 
						|
 | 
						|
    # Open firewall if requested
 | 
						|
    networking.firewall = lib.mkMerge [
 | 
						|
      (lib.mkIf cfg.openFirewall {
 | 
						|
        allowedTCPPorts = [cfg.port];
 | 
						|
      })
 | 
						|
      (lib.mkIf (cfg.downstreams.host.enable && cfg.downstreams.host.openFirewall) {
 | 
						|
        allowedUDPPorts = [cfg.downstreams.host.port];
 | 
						|
      })
 | 
						|
    ];
 | 
						|
  };
 | 
						|
}
 |