DNSvizor

Privacy-enhanced, secure and robust DNS resolver and DHCP server with a small resource footprint as a MirageOS unikernel

Declared in: projects/DNSvizor/default.nix

Try the service in a VM

  1. Install Nix
    Bash
    $ apt install --yes curl git jq nix
    Bash
    $ apt install --yes curl git jq nix
    Bash
    $ pacman --sync --refresh --noconfirm curl git jq nix
  2. Download a configuration file
    # default.nix
    {
      ngipkgs ? import (fetchTarball "https://github.com/ngi-nix/ngipkgs/tarball/main") { },
    }:
    ngipkgs.demo-vm (
      { ... }:
    
      {
        services.dnsvizor = {
          enable = true;
          memory = 128;
          mainInterface = "enp1s0";
          settings = {
            hostname = "dnsvizor.mydomain.example";
            ipv4 = "10.0.0.2/24";
            ipv4-gateway = "10.0.0.1";
            ipv6 = "fdc9:281f:4d7:9ee9::2/64";
            ipv6-gateway = "fdc9:281f:4d7:9ee9::1";
            ca-seed = "Te9ffyY3Clcaz/4P7eFLyZQfLWIz/fSSK4NDb8THMDc=";
            password = "password";
            dns-block = [
              "block1.cli.example.com"
              "block2.cli.example.com"
            ];
            dns-blocklist-url = [
              "http://10.0.0.1/block-list-4"
              "http://[fdc9:281f:4d7:9ee9::1]:80/block-list-6"
              "https://example.com/non-existent-block-list"
            ];
            qname-minimisation = true;
            opportunistic-tls-authoritative = true;
          };
          openFirewall = true;
        };
      }
    )
    
  3. Enable binary substituters
    Bash
    $ export NIX_CONFIG='substituters = https://cache.nixos.org/ https://ngi.cachix.org/
    trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= ngi.cachix.org-1:n+CAL72ROC3qQuLxIHpV+Tw5t42WhXmMhprAGkRSrOw='
  4. Build and run a virtual machine
    Bash
    $ nix-build ./default.nix && ./result
    Bash
    $ nix-build ./default.nix && ./result
    Bash
    $ rev=$(nix-instantiate --eval --attr sources.nixpkgs.rev https://github.com/ngi-nix/ngipkgs/archive/master.tar.gz | jq --raw-output)
    $ nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/$rev.tar.gz --packages nix --run "nix-build ./default.nix && ./result"
    Bash
    $ nix-build ./default.nix && ./result
  5. Usage Instructions
    1. Visit https://127.0.0.1:4443 in your browser. This is a web interface of DNSvizor.

      You'll see a warning of potential security risk. Rest assured. This is because DNSvizor uses a self-signed certification.

      Accept the risk and continue.

    2. In the demo VM terminal, run this to send DNS queries to DNSvizor (10.0.0.2):

      $ q --verbose www.example.com A @10.0.0.2
      

      If the query timeouts, re-run the command to query again.

    3. You can also use an IPv6 address for DNSvizor (fdc9:281f:4d7:9ee9::2):

      $ q --verbose www.example.com A @fdc9:281f:4d7:9ee9::2
      

    4. You can also use a domain name for DNSvizor (dnsvizor.mydomain.example):

      $ q --verbose www.example.com A @dnsvizor.mydomain.example
      

    5. Send encrypted DNS queries using DNS-over-TLS:

      $ q --verbose www.example.com A @tls://dnsvizor.mydomain.example
      

      You'll see an error telling you that certification verification failed. This is because DNSvizor uses a self-signed certification, which is not trusted by default. You can ignore that error by adding --tls-insecure-skip-verify:

      $ q --verbose --tls-insecure-skip-verify www.example.com A @tls://dnsvizor.mydomain.example
      

    6. Let's show you how to trust that self-signed certification of DNSvizor.

      First, we extract that certification using curl.

      $ curl --write-out %{certs} https://dnsvizor.mydomain.example > /tmp/self-signed-cert.pem
      

      Then we setting environment variable SSL_CERT_FILE before running q.

      $ SSL_CERT_FILE=/tmp/self-signed-cert.pem q --verbose www.example.com A @tls://dnsvizor.mydomain.example
      

    7. Send encrypted DNS queries using DNS-over-HTTPS:

      $ SSL_CERT_FILE=/tmp/self-signed-cert.pem q --verbose www.example.com A @https://dnsvizor.mydomain.example
      

    8. Go back to the Dashboard page, you should see those numbers, such as Total queries, have changed.

    9. Go to the Query log page, you should see domains you just queried.

    10. DNSvizor supports blocking DNS resolution for some domains. You can specify them as boot parameters.

      Go to the Blocklist page. Enter the password password. User can be anything you like. You can see some blocked domains we already specified.

      Query one of them:

      $ q --verbose --format raw block1.cli.example.com A @dnsvizor.mydomain.example
      

      You should see status: NXDOMAIN and ANSWER: 0 in the output. This means there is no answer to your DNS query. You should also see appears in blocklist boot-parameter in the output.

    11. Blocked domains can also be specified using URLs. DNSvizor will fetch them using those URLs.

      Go to the Blocklist page, you can see some blocked domain lists we already specified.

      Query one of them:

      $ q --verbose --format raw block1.url.example.com A @dnsvizor.mydomain.example
      

      You should see status: NXDOMAIN and ANSWER: 0 in the output. You should also see appears in blocklist http://10.0.0.1/block-list-4 in the output.

    12. In the Blocklist page, you can also add or delete blocked domains.

      Add one, query it and check the result.

      You should see a similar output as before. But this time, it shows appears in blocklist web-ui.

      Delete the domain you just added, query it and check the result.

      You should see the normal result again.

    13. Now go back to the Dashboard page, you should see the number of "Queries blocked" has changed.

Options

services.dnsvizor
services.dnsvizor.enable

Whether to enable dnsvizor.

Type:
boolean
Default:
false
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.mainInterface

The main network interface of the host.

Type:
string
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.memory

Memory limit of the unikernel in MB.

Type:
positive integer, meaning >0
Default:
512
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.openFirewall

Whether to enable opening ports in the firewall for dnsvizor.

Type:
boolean
Default:
false
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.package

The dnsvizor (hvt target) package to use. We assume dnsvizor.hvt exists at the root dir of the package.

Type:
package
Default:
pkgs.dnsvizor.hvt
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.packetForwardingIsSecure

Whether efforts have been taken to make sure packet forwarding is secure.

Type:
boolean
Default:
config.networking.firewall.enable && config.networking.firewall.filterForward
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings

Configuration for the unikernel. See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

Type:
open submodule of attribute set of (null or boolean or string or list of string)
Default:
{ }
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings.ca-seed

The seed (base64 encoded) used to generate the private key for the certificate. See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

::: {.warning} This secret will be copied into the nix store in clear text. :::

Type:
null or string
Default:
null
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings.dns-block

Domains to block. See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

Type:
list of string
Default:
[ ]
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings.dns-blocklist-url

Web addresses to fetch DNS block lists from. See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

Type:
list of string
Default:
[ ]
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings.dns-upstream

Upstream DNS resolver. By default, it runs as a recursive DNS resolver. If this is specified, it runs as a stub DNS resolver instead. See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

Type:
null or string
Default:
null
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings.help

Show help instead of running the unikernel. See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

Type:
boolean
Default:
false
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings.hostname

The hostname (SNI for the certificate, entry in DNS) of the unikernel. See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

Type:
null or string
Default:
null
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings.https-port

The HTTPS port. See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

Type:
16 bit unsigned integer; between 0 and 65535 (both inclusive)
Default:
443
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings.ipv4

IPv4 network address and prefix length for the unikernel. See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

Type:
string
Default:
"10.0.0.2/24"
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings.ipv4-gateway

IPv4 gateway of the unikernel. See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

Type:
string
Default:
"10.0.0.1"
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings.ipv4-only

Only use IPv4 for the unikernel. See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

Type:
null or one of "true", "false"
Default:
null
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings.ipv6

IPv6 network address and prefix length for the unikernel. See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

Type:
null or string
Default:
null
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings.ipv6-gateway

IPv6 gateway of the unikernel. See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

Type:
null or string
Default:
null
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings.ipv6-only

Only use IPv6 for the unikernel. See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

Type:
null or one of "true", "false"
Default:
null
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings.no-hosts

Don't read the synthesized /etc/hosts which contains only {option}services.dnsvizor.hostname. See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

Type:
boolean
Default:
false
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings.no-tls

Disable TLS: web interface and DNS-over-TLS/DNS-over-HTTPS. See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

Type:
boolean
Default:
config.services.dnsvizor.settings.ca-seed == null
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings.opportunistic-tls-authoritative

Use opportunistic TLS from recursive resolver to authoriative (RFC 9539). See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

Type:
boolean
Default:
false
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings.password

Password used for authentication. See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

::: {.tip} The space character needs to be escaped with \\. :::

::: {.warning} This secret will be copied into the nix store in clear text. :::

Type:
null or string
Default:
null
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix
services.dnsvizor.settings.qname-minimisation

Use qname minimisation (RFC 9156). See upstream online documentation for more information. Setting {option}services.dnsvizor.settings.help shows the help message locally at runtime.

Type:
boolean
Default:
false
Declared in:
projects/DNSvizor/services/dnsvizor/module.nix

Examples

Enable DNSvizor as a IPv4-only stub DNS resolver
{ ... }:

{
  services.dnsvizor = {
    enable = true;
    memory = 128;
    mainInterface = "enp1s0";
    settings = {
      ipv4 = "10.0.0.2/24";
      ipv4-gateway = "10.0.0.1";
      ipv4-only = "true";
      ca-seed = "Te9ffyY3Clcaz/4P7eFLyZQfLWIz/fSSK4NDb8THMDc=";
      password = "password";
      dns-block = [
        "block1.cli.example.com"
        "block2.cli.example.com"
      ];
      dns-blocklist-url = [
        "http://10.0.0.1/block-list"
        "https://example.com/non-existent-block-list"
      ];
      dns-upstream = "tls:1.1.1.1";
    };
  };
}

Declared in: projects/DNSvizor/services/dnsvizor/examples/stub-dns-resolver.nix

Enable DNSvizor as a dual-stack recursive DNS resolver
{ ... }:

{
  services.dnsvizor = {
    enable = true;
    memory = 128;
    mainInterface = "enp1s0";
    settings = {
      hostname = "dnsvizor.mydomain.example";
      ipv4 = "10.0.0.2/24";
      ipv4-gateway = "10.0.0.1";
      ipv6 = "fdc9:281f:4d7:9ee9::2/64";
      ipv6-gateway = "fdc9:281f:4d7:9ee9::1";
      ca-seed = "Te9ffyY3Clcaz/4P7eFLyZQfLWIz/fSSK4NDb8THMDc=";
      password = "password";
      dns-block = [
        "block1.cli.example.com"
        "block2.cli.example.com"
      ];
      dns-blocklist-url = [
        "http://10.0.0.1/block-list-4"
        "http://[fdc9:281f:4d7:9ee9::1]:80/block-list-6"
        "https://example.com/non-existent-block-list"
      ];
      qname-minimisation = true;
      opportunistic-tls-authoritative = true;
    };
    openFirewall = true;
  };
}

Declared in: projects/DNSvizor/services/dnsvizor/examples/recursive-dns-resolver.nix

Metadata

This project is funded by NLnet through these subgrants:

Entrust
DNSvizor

Related links: