diff --git a/nixos/doc/manual/redirects.json b/nixos/doc/manual/redirects.json index 6f897ce7a9917..f792750a1ea4d 100644 --- a/nixos/doc/manual/redirects.json +++ b/nixos/doc/manual/redirects.json @@ -2,6 +2,45 @@ "book-nixos-manual": [ "index.html#book-nixos-manual" ], + "module-services-crab-hole": [ + "index.html#module-services-crab-hole" + ], + "module-services-crab-hole-api": [ + "index.html#module-services-crab-hole-api" + ], + "module-services-crab-hole-configuration": [ + "index.html#module-services-crab-hole-configuration" + ], + "module-services-crab-hole-dnssec": [ + "index.html#module-services-crab-hole-dnssec" + ], + "module-services-crab-hole-downstream": [ + "index.html#module-services-crab-hole-downstream" + ], + "module-services-crab-hole-https": [ + "index.html#module-services-crab-hole-https" + ], + "module-services-crab-hole-invalid-config": [ + "index.html#module-services-crab-hole-invalid-config" + ], + "module-services-crab-hole-permission-error": [ + "index.html#module-services-crab-hole-permission-error" + ], + "module-services-crab-hole-quic": [ + "index.html#module-services-crab-hole-quic" + ], + "module-services-crab-hole-tls": [ + "index.html#module-services-crab-hole-tls" + ], + "module-services-crab-hole-troubleshooting": [ + "index.html#module-services-crab-hole-troubleshooting" + ], + "module-services-crab-hole-udp": [ + "index.html#module-services-crab-hole-udp" + ], + "module-services-crab-hole-upstream-options": [ + "index.html#module-services-crab-hole-upstream-options" + ], "preface": [ "index.html#preface" ], diff --git a/nixos/doc/manual/release-notes/rl-2505.section.md b/nixos/doc/manual/release-notes/rl-2505.section.md index fd04254871ede..edfbb8fe5a9aa 100644 --- a/nixos/doc/manual/release-notes/rl-2505.section.md +++ b/nixos/doc/manual/release-notes/rl-2505.section.md @@ -20,6 +20,8 @@ - [Traccar](https://www.traccar.org/), a modern GPS Tracking Platform. Available as [services.traccar](#opt-services.traccar.enable). +- [crab-hole](https://github.com/LuckyTurtleDev/crab-hole), a cross platform Pi-hole clone written in Rust using hickory-dns/trust-dns. Available as [services.crab-hole](#opt-services.crab-hole.enable). + - [Amazon CloudWatch Agent](https://github.com/aws/amazon-cloudwatch-agent), the official telemetry collector for AWS CloudWatch and AWS X-Ray. Available as [services.amazon-cloudwatch-agent](options.html#opt-services.amazon-cloudwatch-agent.enable). - [agorakit](https://github.com/agorakit/agorakit), an organization tool for citizens' collectives. Available with [services.agorakit](options.html#opt-services.agorakit.enable). diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index b285e3f7c7aeb..b12da3f575fb3 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1027,6 +1027,7 @@ ./services/networking/coredns.nix ./services/networking/corerad.nix ./services/networking/coturn.nix + ./services/networking/crab-hole.nix ./services/networking/create_ap.nix ./services/networking/croc.nix ./services/networking/dae.nix diff --git a/nixos/modules/services/networking/crab-hole.md b/nixos/modules/services/networking/crab-hole.md new file mode 100644 index 0000000000000..3226caec25455 --- /dev/null +++ b/nixos/modules/services/networking/crab-hole.md @@ -0,0 +1,215 @@ +# 🦀 crab-hole {#module-services-crab-hole} + +Crab-hole is a cross platform Pi-hole clone written in Rust using [hickory-dns/trust-dns](https://github.com/hickory-dns/hickory-dns). +It can be used as a network wide ad and spy blocker or run on your local PC. + +For a secure and private communication, crab-hole has builtin support for DoH(HTTPS), DoQ(QUIC) and DoT(TLS) for down- and upstreams and DNSSEC for upstreams. +It also comes with privacy friendly default logging settings. + +## Configuration {#module-services-crab-hole-configuration} +As an example config file using Cloudflare as DoT upstream, you can use this [crab-hole.toml](https://github.com/LuckyTurtleDev/crab-hole/blob/main/example-config.toml) + + +The following is a basic nix config using UDP as a downstream and Cloudflare as upstream. + +```nix +{ + services.crab-hole = { + enable = true; + + settings = { + blocklist = { + include_subdomains = true; + lists = [ + "https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/fakenews-gambling-porn/hosts" + "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt" + ]; + }; + + downstream = [ + { + protocol = "udp"; + listen = "127.0.0.1"; + port = 53; + } + { + protocol = "udp"; + listen = "::1"; + port = 53; + } + ]; + + 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 = "[2606:4700:4700::1111]:853"; + protocol = "tls"; + tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com"; + trust_nx_responses = false; + } + ]; + }; + }; + }; +} +``` + +To test your setup, just query the DNS server with any domain like `example.com`. +To test if a domain gets blocked, just choose one of the domains from the blocklist. +If the server does not return an IP, this worked correctly. + +### Downstream options {#module-services-crab-hole-downstream} +There are multiple protocols which are supported for the downstream: +UDP, TLS, HTTPS and QUIC. +Below you can find a brief overview over the various protocol options together with an example for each protocol. + +#### UDP {#module-services-crab-hole-udp} +UDP is the simplest downstream, but it is not encrypted. +If you want encryption, you need to use another protocol. +***Note:** This also opens a TCP port* +```nix +{ + services.crab-hole.settings.downstream = [ + { + protocol = "udp"; + listen = "localhost"; + port = 53; + } + ]; +} +``` + +#### TLS {#module-services-crab-hole-tls} +TLS is a simple encrypted options to serve DNS. +It comes with similar settings to UDP, +but you additionally need a valid TLS certificate and its private key. +The later are specified via a path to the files. +A valid TLS certificate and private key can be obtained using services like ACME. +Make sure the crab-hole service user has access to these files. +Additionally you can set an optional timeout value. +```nix +{ + services.crab-hole.settings.downstream = [ + { + protocol = "tls"; + listen = "[::]"; + port = 853; + certificate = ./dns.example.com.crt; + key = "/dns.example.com.key"; + # optional (default = 3000) + timeout_ms = 3000 + } + ]; +} +``` + +#### HTTPS {#module-services-crab-hole-https} +HTTPS has similar settings to TLS, with the only difference being the additional `dns_hostname` option. +This protocol might need a reverse proxy if other HTTPS services are to share the same port. +Make sure the service has permissions to access the certificate and key. + +***Note:** this config is untested* +```nix +{ + services.crab-hole.settings.downstream = [ + { + protocol = "https"; + listen = "[::]"; + port = 443; + certificate = ./dns.example.com.crt; + key = "/dns.example.com.key"; + # optional + dns_hostname = "dns.example.com"; + # optional (default = 3000) + timeout_ms = 3000; + } + ]; +} +``` + +#### QUIC {#module-services-crab-hole-quic} +QUIC has identical settings to the HTTPS protocol. +Since by default it doesn't run on the standard HTTPS port, you shouldn't need a reverse proxy. +Make sure the service has permissions to access the certificate and key. +```nix +{ + services.crab-hole.settings.downstream = [ + { + protocol = "quic"; + listen = "127.0.0.1"; + port = 853; + certificate = ./dns.example.com.crt; + key = "/dns.example.com.key"; + # optional + dns_hostname = "dns.example.com"; + # optional (default = 3000) + timeout_ms = 3000; + } + ]; +} +``` + +### Upstream options {#module-services-crab-hole-upstream-options} +You can set additional options of the underlying DNS server. A full list of all the options can be found in the [hickory-dns documentation](https://docs.rs/trust-dns-resolver/0.23.0/trust_dns_resolver/config/struct.ResolverOpts.html). + +This can look like the following example. +```nix +{ + services.crab-hole.settings.upstream.options = { + validate = false; + }; +} +``` + +#### DNSSEC Issues {#module-services-crab-hole-dnssec} +Due to an upstream issue of [hickory-dns](https://github.com/hickory-dns/hickory-dns/issues/2429), sites without DNSSEC will not be resolved if `validate = true`. +Only DNSSEC capable sites will be resolved with this setting. +To prevent this, set `validate = false` or omit the `[upstream.options]`. + +### API {#module-services-crab-hole-api} +The API allows a user to fetch statistic and information about the crab-hole instance. +Basic information is availablee for everyone, while more detailed information is secured by a key, which will be set with the `admin_key` option. + +```nix +{ + services.crab-hole.settings.api = { + listen = "127.0.0.1"; + port = 8080; + # optional (default = false) + show_doc = true; # OpenAPI doc loads content from third party websites + # optional + admin_key = "1234"; + }; +} + +``` + +The documentation can be enabled seperately for the instance with `show_doc`. +This will then create an additional webserver, which hosts the API documentation. +An additional resource is in work in the [crab-hole repository](https://github.com/LuckyTurtleDev/crab-hole). + +## Troubleshooting {#module-services-crab-hole-troubleshooting} +You can check for errors using `systemctl status crab-hole` or `journalctl -xeu crab-hole.service`. + +### Invalid config {#module-services-crab-hole-invalid-config} +Some options of the service are in freeform and not type checked. +This can lead to a config which is not valid or cannot be parsed by crab-hole. +The error message will tell you what config value could not be parsed. +For more information check the [example config](https://github.com/LuckyTurtleDev/crab-hole/blob/main/example-config.toml). + +### Permission Error {#module-services-crab-hole-permission-error} +It can happen that the created certificates for TLS, HTTPS or QUIC are owned by another user or group. +For ACME for example this would be `acme:acme`. +To give the crab-hole service access to these files, the group which owns the certificate can be added as a supplementary group to the service. +For ACME for example: +```nix +{ + services.crab-hole.supplementaryGroups = [ "acme" ]; +} +``` diff --git a/nixos/modules/services/networking/crab-hole.nix b/nixos/modules/services/networking/crab-hole.nix new file mode 100644 index 0000000000000..7e68c649ad5b2 --- /dev/null +++ b/nixos/modules/services/networking/crab-hole.nix @@ -0,0 +1,180 @@ +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.services.crab-hole; + + settingsFormat = pkgs.formats.toml { }; + + checkConfig = + file: + pkgs.runCommand "check-config" + { + nativeBuildInputs = [ + cfg.package + pkgs.cacert + pkgs.dig + ]; + } + '' + ln -s ${file} $out + + ln -s ${file} ./config.toml + export CRAB_HOLE_DIR=$(pwd) + + ${lib.getExe cfg.package} validate-config + ''; +in +{ + options = { + services.crab-hole = { + enable = lib.mkEnableOption "Crab-hole Service"; + + package = lib.mkPackageOption pkgs "crab-hole" { }; + + supplementaryGroups = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + example = [ "acme" ]; + description = "Adds additional groups to the crab-hole service. Can be useful to prevent permission issues."; + }; + + settings = lib.mkOption { + description = "Crab-holes config. See big example https://github.com/LuckyTurtleDev/crab-hole/blob/main/example-config.toml"; + + example = { + downstream = [ + { + listen = "localhost"; + port = 8080; + protocol = "udp"; + } + { + certificate = "dns.example.com.crt"; + dns_hostname = "dns.example.com"; + key = "dns.example.com.key"; + listen = "[::]"; + port = 8055; + protocol = "https"; + timeout_ms = 3000; + } + ]; + api = { + admin_key = "1234"; + listen = "127.0.0.1"; + port = 8080; + show_doc = true; + }; + blocklist = { + allow_list = [ + "file:///allowed.txt" + ]; + include_subdomains = true; + lists = [ + "https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/fakenews-gambling-porn/hosts" + "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt" + "file:///blocked.txt" + ]; + }; + upstream = { + name_servers = [ + { + protocol = "tls"; + socket_addr = "[2606:4700:4700::1111]:853"; + tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com"; + trust_nx_responses = false; + } + { + protocol = "tls"; + socket_addr = "1.1.1.1:853"; + tls_dns_name = "1dot1dot1dot1.cloudflare-dns.com"; + trust_nx_responses = false; + } + ]; + options = { + validate = false; + }; + }; + }; + + type = lib.types.submodule { + freeformType = settingsFormat.type; + options = { + blocklist = + let + listOption = + name: + lib.mkOption { + type = lib.types.listOf (lib.types.either lib.types.str lib.types.path); + default = [ ]; + description = "List of ${name}. If files are added via url, make sure the service has access to them!"; + apply = map (v: if builtins.isPath v then "file://${v}" else v); + }; + in + { + include_subdomains = lib.mkEnableOption "Include subdomains"; + lists = listOption "blocklists"; + allow_list = listOption "allowlists"; + }; + }; + }; + }; + + configFile = lib.mkOption { + type = lib.types.path; + description = '' + The config file of crab-hole. + + If files are added via url, make sure the service has access to them. + Setting this option will override any configuration applied by the settings option. + ''; + }; + }; + }; + + config = lib.mkIf cfg.enable { + # Warning due to DNSSec issue in crab-hole + warnings = lib.optional (cfg.settings.upstream.options.validate or false) '' + Validate options will ONLY allow DNSSec domains. See https://github.com/LuckyTurtleDev/crab-hole/issues/29 + ''; + + services.crab-hole.configFile = lib.mkDefault ( + checkConfig (settingsFormat.generate "crab-hole.toml" cfg.settings) + ); + environment.etc."crab-hole.toml".source = cfg.configFile; + + systemd.services.crab-hole = { + wantedBy = [ "multi-user.target" ]; + after = [ "network-online.target" ]; + wants = [ "network-online.target" ]; + description = "Crab-hole dns server"; + environment.HOME = "/var/lib/crab-hole"; + restartTriggers = [ cfg.configFile ]; + serviceConfig = { + Type = "simple"; + DynamicUser = true; + SupplementaryGroups = cfg.supplementaryGroups; + + StateDirectory = "crab-hole"; + WorkingDirectory = "/var/lib/crab-hole"; + + ExecStart = lib.getExe cfg.package; + + AmbientCapabilities = "CAP_NET_BIND_SERVICE"; + CapabilityBoundingSet = "CAP_NET_BIND_SERVICE"; + + Restart = "on-failure"; + RestartSec = 1; + }; + }; + }; + + meta.maintainers = [ + lib.maintainers.NiklasVousten + ]; + # Readme from upstream + meta.doc = ./crab-hole.md; +}