clan: import @shallerclan/dns service
This commit is contained in:
parent
eb80f8089c
commit
1dec333f3f
2 changed files with 312 additions and 0 deletions
310
clanServices/dns.nix
Normal file
310
clanServices/dns.nix
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
{ ... }:
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "dns";
|
||||
manifest.categories = [ "Network" ];
|
||||
manifest.description = "Clan-internal DNS and service exposure";
|
||||
manifest.readme = ''
|
||||
# How it works
|
||||
|
||||
Every dns-request from a clan machine lands at systemd-resolved and it resolves (forwards) requests with the following priority:
|
||||
|
||||
1. /etc/hosts file
|
||||
2. Local authority nameserver (if tld is ''${config.clan.core.settings.domain})
|
||||
3. Configured system's dns-servers e.g. `networking.nameservers`
|
||||
|
||||
The local authority nameserver is configured to answer requests only from localhost and it hosts the zonefile for the clan domain.
|
||||
|
||||
For external requests, the server role must be deployed/configured.
|
||||
|
||||
# Usage
|
||||
|
||||
```nix
|
||||
# clan.nix
|
||||
inventory.instances.dns = {
|
||||
module.input = "self";
|
||||
module.name = "@schallerclan/dns";
|
||||
|
||||
roles.server = {
|
||||
tags = [ "serve_dns" ];
|
||||
extraModules = [ modules/blocky.nix ];
|
||||
|
||||
machines."myMachine01" = {};
|
||||
};
|
||||
|
||||
roles.default.machines."myMachine01".settings = {
|
||||
records = {
|
||||
A = [
|
||||
"203.0.113.1" # www
|
||||
"10.0.0.1" # wireguard
|
||||
"100.0.0.1" # tailscale
|
||||
];
|
||||
AAAA = [
|
||||
"2001:db8::1" # www
|
||||
"fc00::1" # wireguard
|
||||
"fd00::1" # tailscale
|
||||
"400::1" # mycelium
|
||||
"200::1" # yggdrasil
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
roles.default.machines."myMachine02".settings = {
|
||||
records = {
|
||||
A = [
|
||||
"203.0.113.2" # www
|
||||
"10.0.0.2" # wireguard
|
||||
"100.0.0.2" # tailscale
|
||||
];
|
||||
AAAA = [
|
||||
"2001:db8::2" # www
|
||||
"fc00::2" # wireguard
|
||||
"fd00::2" # tailscale
|
||||
"400::2" # mycelium
|
||||
"200::2" # yggdrasil
|
||||
];
|
||||
};
|
||||
|
||||
services = [ "foo" ];
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
The example will result into the following records, in the zonefile:
|
||||
|
||||
```
|
||||
myMachine01.clan A 203.0.113.1
|
||||
myMachine01.clan A 10.0.0.1
|
||||
myMachine01.clan A 100.0.0.1
|
||||
myMachine01.clan AAAA 2001:db8::1
|
||||
myMachine01.clan AAAA fd00::1
|
||||
myMachine01.clan AAAA fc00::1
|
||||
myMachine01.clan AAAA 400::1
|
||||
myMachine01.clan AAAA 200::1
|
||||
|
||||
myMachine02.clan A 203.0.113.2
|
||||
myMachine02.clan A 10.0.0.2
|
||||
myMachine02.clan A 100.0.0.2
|
||||
myMachine02.clan AAAA 2001:db8::2
|
||||
myMachine02.clan AAAA fd00::2
|
||||
myMachine02.clan AAAA fc00::2
|
||||
myMachine02.clan AAAA 400::2
|
||||
myMachine02.clan AAAA 200::2
|
||||
|
||||
foo.clan A 203.0.113.2
|
||||
foo.clan A 10.0.0.2
|
||||
foo.clan A 100.0.0.2
|
||||
foo.clan AAAA 2001:db8::2
|
||||
foo.clan AAAA fd00::2
|
||||
foo.clan AAAA fc00::2
|
||||
foo.clan AAAA 400::2
|
||||
foo.clan AAAA 200::2
|
||||
```
|
||||
'';
|
||||
|
||||
roles.default = {
|
||||
description = ''
|
||||
Machines in this role will take part in dns.
|
||||
|
||||
Machines will
|
||||
- register their hostname with the configured records
|
||||
- register their `settings.services` with the configured records
|
||||
- be able to resolve all records locally
|
||||
'';
|
||||
|
||||
interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
options = with lib.types; {
|
||||
records = lib.mkOption {
|
||||
type = attrsOf (coercedTo str (s: [ s ]) (listOf str));
|
||||
default = { };
|
||||
description = ''
|
||||
DNS records for the machine and all its services.
|
||||
|
||||
Technically, no restrictions on which dns records can be used. But
|
||||
intended for A and AAAA records.
|
||||
'';
|
||||
example = {
|
||||
A = [
|
||||
"203.0.113.2" # www
|
||||
"10.0.0.2" # wireguard
|
||||
"100.0.0.2" # tailscale
|
||||
];
|
||||
AAAA = [
|
||||
"2001:db8::2" # www
|
||||
"fd28:387a:6e:df00::2" # wireguard
|
||||
"fd7a:115c:a1e0::2" # tailscale
|
||||
"400::2" # mycelium
|
||||
"200::2" # yggdrasil
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
services = lib.mkOption {
|
||||
type = listOf str;
|
||||
default = [ ];
|
||||
description = ''
|
||||
Service endpoints this host exposes (without TLD). Each entry will be resolved to <service>.''${config.clan.core.settings.domain}.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
perInstance =
|
||||
{ roles, settings, ... }:
|
||||
{
|
||||
nixosModule =
|
||||
{
|
||||
lib,
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
{
|
||||
networking.nameservers = [ "[::1]:1053#${config.clan.core.settings.domain}" ];
|
||||
|
||||
services.resolved.domains = [ "~${config.clan.core.settings.domain}" ];
|
||||
|
||||
services.unbound = {
|
||||
enable = true;
|
||||
|
||||
# https://unbound.docs.nlnetlabs.nl/en/latest/manpages/unbound.conf.html
|
||||
settings = {
|
||||
server = {
|
||||
port = 1053;
|
||||
verbosity = 2;
|
||||
interface = [
|
||||
"127.0.0.1"
|
||||
"::1"
|
||||
];
|
||||
access-control = [
|
||||
"127.0.0.0/8 allow"
|
||||
"::0/64 allow"
|
||||
];
|
||||
do-not-query-localhost = false;
|
||||
domain-insecure = [ "${config.clan.core.settings.domain}." ];
|
||||
};
|
||||
|
||||
auth-zone = [
|
||||
{
|
||||
name = config.clan.core.settings.domain;
|
||||
zonefile = "${pkgs.writeTextFile (
|
||||
let
|
||||
nsRecords = lib.lists.concatLists (
|
||||
# ↓ this machine
|
||||
lib.lists.forEach (lib.attrsToList settings.records) (
|
||||
record: lib.lists.forEach record.value (value: "ns ${record.name} ${value}")
|
||||
)
|
||||
);
|
||||
|
||||
machineRecords = lib.lists.concatLists (
|
||||
lib.lists.forEach (lib.attrNames roles.default.machines) (
|
||||
machine:
|
||||
lib.lists.concatLists (
|
||||
lib.lists.forEach (lib.attrsToList roles.default.machines.${machine}.settings.records) (
|
||||
record: lib.lists.forEach record.value (value: "${machine} ${record.name} ${value}")
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
serviceRecords = lib.lists.concatLists (
|
||||
lib.lists.forEach (lib.attrNames roles.default.machines) (
|
||||
machine:
|
||||
lib.lists.concatLists (
|
||||
lib.lists.forEach roles.default.machines.${machine}.settings.services (
|
||||
service:
|
||||
lib.lists.concatLists (
|
||||
lib.lists.forEach (lib.attrsToList roles.default.machines.${machine}.settings.records) (
|
||||
record: lib.lists.forEach record.value (value: "${service} ${record.name} ${value} ; ${machine}")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
in
|
||||
{
|
||||
name = "db.${config.clan.core.settings.domain}.zone";
|
||||
text = lib.strings.concatStringsSep "\n\n" [
|
||||
''
|
||||
$ORIGIN ${config.clan.core.settings.domain}.
|
||||
$TTL 3600
|
||||
@ IN SOA ns admin 1 7200 3600 1209600 3600
|
||||
@ IN NS ns
|
||||
''
|
||||
(lib.strings.concatStringsSep "\n" nsRecords)
|
||||
(lib.strings.concatStringsSep "\n" machineRecords)
|
||||
(lib.strings.concatStringsSep "\n" serviceRecords)
|
||||
];
|
||||
}
|
||||
)}";
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
roles.server = {
|
||||
description = ''
|
||||
Additional role upon `roles.default`.
|
||||
|
||||
Machines in this role will serve [blocky](https://0xerr0r.github.io/blocky/latest/) as a dns server on port 53, including all dns records in the default role.
|
||||
|
||||
For blocky (dns server) configuration, make a new file at e.g. `modules/blocky.nix` and include it with `roles.server.extraModules [ modules/blocky.nix ];`
|
||||
|
||||
```nix
|
||||
# modules/blocky.nix
|
||||
{
|
||||
# https://0xerr0r.github.io/blocky/latest/configuration
|
||||
services.blocky.settings = {
|
||||
# ...
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
With `systemctl status blocky.service` check, if blocky was configured correctly.
|
||||
'';
|
||||
|
||||
perInstance = {
|
||||
nixosModule =
|
||||
{ lib, config, ... }:
|
||||
{
|
||||
networking.firewall.allowedTCPPorts = [ 53 ];
|
||||
networking.firewall.allowedUDPPorts = [ 53 ];
|
||||
|
||||
services.resolved.enable = false;
|
||||
services.blocky = {
|
||||
enable = true;
|
||||
settings = {
|
||||
upstreams.groups.default = lib.mkDefault [
|
||||
# quad9
|
||||
"9.9.9.9"
|
||||
"149.112.112.112"
|
||||
"2620:fe::fe"
|
||||
"2620:fe::9"
|
||||
# cloudflare
|
||||
"1.1.1.1"
|
||||
"1.0.0.1"
|
||||
"2606:4700:4700::1111"
|
||||
"2606:4700:4700::1001"
|
||||
];
|
||||
|
||||
conditional.mapping = {
|
||||
${config.clan.core.settings.domain} = "tcp+udp:[::1]:1053";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
perMachine = {
|
||||
nixosModule = {
|
||||
services.tailscale.extraUpFlags = [ "--accept-dns=false" ];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -6,4 +6,6 @@
|
|||
];
|
||||
|
||||
clan.modules."@rpqt/vaultwarden" = ./vaultwarden.nix;
|
||||
|
||||
clan.modules."@schallerclan/dns" = ./dns.nix;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue