cluster/services/dns: init

This commit is contained in:
Max Headroom 2022-08-07 19:58:37 +02:00
parent 6b998f4ec2
commit 5356ba97c6
12 changed files with 258 additions and 98 deletions

View file

@ -0,0 +1,115 @@
{ cluster, config, hosts, inputs, lib, pkgs, tools, ... }:
let
inherit (hosts.${config.networking.hostName}) interfaces;
inherit (tools.meta) domain;
inherit (config.links) pdnsAdmin;
inherit (cluster.config) vars;
pdns-api = cluster.config.links.powerdns-api;
dataDirUI = "/srv/storage/private/powerdns-admin";
translateConfig = withQuotes: cfg: let
pythonValue = val: if lib.isString val then "'${val}'"
else if lib.isAttrs val && val ? env then "__import__('os').getenv('${val.env}')"
else if lib.isBool val then (if val then "True" else "False")
else if lib.isInt val then toString val
else throw "translateConfig: unsupported value type";
quote = str: if withQuotes then pythonValue str else str;
configList = lib.mapAttrsToList (n: v: "${n}=${quote v}") cfg;
in lib.concatStringsSep "\n" configList;
login = x: "https://login.${domain}/auth/realms/master/protocol/openid-connect/${x}";
in {
age.secrets = {
pdns-admin-oidc-secrets = {
file = ./pdns-admin-oidc-secrets.age;
mode = "0400";
};
pdns-admin-salt = {
file = ./pdns-admin-salt.age;
mode = "0400";
owner = "powerdnsadmin";
group = "powerdnsadmin";
};
pdns-admin-secret = {
file = ./pdns-admin-secret.age;
mode = "0400";
owner = "powerdnsadmin";
group = "powerdnsadmin";
};
pdns-api-key = {
file = ./pdns-api-key.age;
mode = "0400";
};
};
links.pdnsAdmin.protocol = "http";
networking.firewall = {
allowedTCPPorts = [ 53 ];
allowedUDPPorts = [ 53 ];
};
systemd.tmpfiles.rules = [
"d '${dataDirUI}' 0700 powerdnsadmin powerdnsadmin - -"
];
services.powerdns = {
enable = true;
extraConfig = translateConfig false {
api = "yes";
webserver-allow-from = "127.0.0.1, ${vars.meshNet.cidr}";
webserver-address = pdns-api.ipv4;
webserver-port = pdns-api.portStr;
api-key = "$scrypt$ln=14,p=1,r=8$ZRgztsniH1y+F7P/RkXq/w==$QTil5kbJPzygpeQRI2jgo5vK6fGol9YS/NVR95cmWRs=";
};
};
services.powerdns-admin = {
enable = true;
secretKeyFile = config.age.secrets.pdns-admin-secret.path;
saltFile = config.age.secrets.pdns-admin-salt.path;
extraArgs = [ "-b" pdnsAdmin.tuple ];
config = translateConfig true {
SQLALCHEMY_DATABASE_URI = "sqlite:///${dataDirUI}/pda.db";
PDNS_VERSION = pkgs.pdns.version;
PDNS_API_URL = pdns-api.url;
PDNS_API_KEY.env = "PDNS_API_KEY";
SIGNUP_ENABLED = false;
OIDC_OAUTH_ENABLED = true;
OIDC_OAUTH_KEY = "net.privatevoid.dnsadmin1";
OIDC_OAUTH_SECRET.env = "OIDC_OAUTH_SECRET";
OIDC_OAUTH_SCOPE = "openid profile email roles";
OIDC_OAUTH_API_URL = login "";
OIDC_OAUTH_TOKEN_URL = login "token";
OIDC_OAUTH_AUTHORIZE_URL = login "auth";
OIDC_OAUTH_LOGOUT_URL = login "logout";
};
};
systemd.services.powerdns-admin.serviceConfig = {
BindPaths = [ dataDirUI ];
EnvironmentFile = [
config.age.secrets.pdns-api-key.path
config.age.secrets.pdns-admin-oidc-secrets.path
];
};
services.nginx.virtualHosts."dnsadmin.${domain}" = lib.recursiveUpdate
(tools.nginx.vhosts.proxy pdnsAdmin.url)
# backend sends really big headers for some reason
# increase buffer size accordingly
{
locations."/".extraConfig = ''
proxy_busy_buffers_size 512k;
proxy_buffers 4 512k;
proxy_buffer_size 256k;
'';
};
}

View file

@ -0,0 +1,41 @@
{ cluster, config, hosts, inputs, lib, pkgs, tools, ... }:
let
inherit (hosts.${config.networking.hostName}) interfaces;
inherit (cluster.config) vars;
patroni = cluster.config.links.patroni-pg-access;
pdns-api = cluster.config.links.powerdns-api;
translateConfig = cfg: let
configList = lib.mapAttrsToList (n: v: "${n}=${v}") cfg;
in lib.concatStringsSep "\n" configList;
in {
age.secrets = {
pdns-db-credentials = {
file = ./pdns-db-credentials.age;
mode = "0400";
owner = "pdns";
group = "pdns";
};
};
networking.firewall = {
allowedTCPPorts = [ 53 ];
allowedUDPPorts = [ 53 ];
};
services.powerdns = {
enable = true;
extraConfig = translateConfig {
launch = "gpgsql";
local-address = interfaces.primary.addr;
gpgsql-host = patroni.ipv4;
gpgsql-port = patroni.portStr;
gpgsql-dbname = "powerdns";
gpgsql-user = "powerdns";
gpgsql-extra-connection-parameters = "passfile=${config.age.secrets.pdns-db-credentials.path}";
version-string = "Private Void DNS";
};
};
}

View file

@ -1,15 +1,19 @@
{ config, hosts, inputs, pkgs, tools, ... }:
# TODO: is this secure?
let
inherit (hosts.${config.networking.hostName}) interfaces;
inherit (tools.meta) domain;
inherit (config.links) localRecursor;
inherit (inputs.self.packages.${pkgs.system}) stevenblack-hosts;
dot = config.security.acme.certs."securedns.${domain}";
in {
imports = [ ./zones.nix ];
in
{
links.localRecursor = {};
networking.firewall = {
allowedTCPPorts = [ 53 853 ];
allowedUDPPorts = [ 53 853 ];
allowedTCPPorts = [ 853 ];
allowedUDPPorts = [ 853 ];
};
systemd.services.coredns = {
@ -19,6 +23,7 @@ in {
"dot-key.pem:${dot.directory}/key.pem"
];
};
security.acme.certs."securedns.${domain}" = {
group = "nginx";
webroot = "/var/lib/acme/acme-challenge";
@ -28,52 +33,42 @@ in {
"coredns.service"
];
};
services.coredns = {
enable = true;
config = ''
. {
bind ${interfaces.vstub.addr}
hosts ${inputs.self.packages.${pkgs.system}.stevenblack-hosts} {
bind 127.0.0.1
hosts ${stevenblack-hosts} {
fallthrough
}
chaos "Private Void DNS" info@privatevoid.net
forward . 127.0.0.1
forward . ${localRecursor.tuple}
}
tls://.:853 {
bind ${interfaces.primary.addr}
tls {$CREDENTIALS_DIRECTORY}/dot-cert.pem {$CREDENTIALS_DIRECTORY}/dot-key.pem
hosts ${inputs.self.packages.${pkgs.system}.stevenblack-hosts} {
hosts ${stevenblack-hosts} {
fallthrough
}
chaos "Private Void DNS" info@privatevoid.net
forward . ${interfaces.primary.addr}
forward . ${localRecursor.tuple}
}
'';
};
services.bind = {
services.pdns-recursor = {
enable = true;
# TODO: un-hardcode all ip addresses
listenOn = [ interfaces.primary.addr "127.0.0.1" ];
ipv4Only = true;
cacheNetworks = [ "10.0.0.0/8" ];
extraConfig = ''
acl "trusted" {
127.0.0.0/8;
::1/128;
${interfaces.vstub.addr}/32;
10.100.0.0/16;
10.10.0.0/16;
};
acl "publicservers" {
116.202.226.86/32;
};
'';
extraOptions = ''
recursion yes;
allow-recursion { trusted; ${interfaces.primary.addr}/32; };
dnssec-validation no;
'';
dnssecValidation = "process";
forwardZones = {
# optimize queries against our own domain
"${domain}" = interfaces.primary.addr;
};
dns = {
inherit (localRecursor) port;
address = localRecursor.ipv4;
allowFrom = [ "127.0.0.1" ];
};
};
}

View file

@ -0,0 +1,32 @@
{ config, ... }:
let
inherit (config.vars) hosts;
in
{
links = {
dnsResolver = {
ipv4 = hosts.VEGAS.interfaces.vstub.addr;
port = 53;
};
powerdns-api = {
ipv4 = config.vars.mesh.VEGAS.meshIp;
protocol = "http";
};
};
services.dns = {
nodes = {
master = [ "VEGAS" ];
slave = [ "prophet" ];
coredns = [ "VEGAS" ];
};
nixos = {
master = [
./authoritative.nix
./admin.nix
];
slave = ./authoritative.nix;
coredns = ./coredns.nix;
};
};
}

Binary file not shown.

View file

@ -0,0 +1,11 @@
age-encryption.org/v1
-> ssh-ed25519 NO562A d/YNanH/cHoFLPp8WcCXHh/LQLRwaUa95JiRLbgb8RI
UPEHpnHHTU6dGKi2MbApEspcpt1lFtFZ4XJjShL7OoE
-> ssh-ed25519 5/zT0w Rv9ZS5P2Eca3npPLR7yym/XTRSDfVmgRwH1pAGR79T8
4A/KXc2wxxokfDAwWYf0ZTUEzQ8ldkC+zRNZY3KjBTs
-> ssh-ed25519 d3WGuA 2R0kaVjuhU3wT9pjj214zkEaHYNSlMxf9Z+MfBssHwY
EU5LWk6xfohWM/3sAqYtUvFmRgIPxOLXHnlqbsQ3+ok
-> -|(-grease W=cc~ O2q5
FZzh/ZwDS2EqvVZ9NErmUwCMN72op1Qy
--- Ducan3ugRJC3dmWLr7+FKok+WmInOgOzW0ccYeqAFAQ
Ì•ãÆ*Q. SC<53>ûf¹‰*`5<>„ÑÖw"~ÍxwÜ*–ã\êÙ"²ÅtŒ 'É0ï™<C3AF>ï

View file

@ -0,0 +1,12 @@
age-encryption.org/v1
-> ssh-ed25519 NO562A hUR+UdHnpazhANM8DKToI5Th3lv1aAuxZ1IQKvCOv34
PvsiSym8YdleDULLnWuTs1x08KO3EmAg/AAjulgrgqE
-> ssh-ed25519 5/zT0w qMXS2xLOLv/+l6brG11i+3FwHdrhlmxZBNtBiU9hu2g
BlFYPvH4mFJRMHTlHwnBdJb6QcugylwZuT5bgSKcQa0
-> ssh-ed25519 d3WGuA k2fRQ3+HyZP+bb/gkVKQqUmbITJLPm9tGp67DbRfiCs
RX9CACfYpYKvSqyfXjvEokTGsp4+ECQBD8i1ehD5xRg
-> IB@F$9G-grease
cXRgUVdIPGEjft1CJA
--- si16Det/GwF7GLHLt0ha8v4rFFeJXyhEylIiqzZVAK8
Ö°å¤pÐǺ#ê4^©— ~u Uuç­aòQ´Bâj˜(N)qÃ<"¤%ì’,V9û5ZÔh§#W«[»ò¶”"Mÿ&”îäøÖýá+%Œ«„SQ€B÷ÞÕÀèÕyàÜî<aéó]P$´Ä±B¨½qQÑÉQ‡M‰TË
·s¹mÿ~qWÖ«çêõÜ×Ì=.Q“"ù”Þø¶ÏnqRk<52>=ÏcÿçüßÃqv¢¾>#ŠÏ«²tïwq,÷ »3YyIq}Ê“ì>sgíz™ûs±Þ ¸ƆFÄPê|ÍüÅ¡=ùÃþ~KQR,DZuÐ+ÕºZGHëa=‹©;ÀõC.ÏuVShÅ$Và€AË9Ð= ?•¢

Binary file not shown.

View file

@ -0,0 +1,15 @@
age-encryption.org/v1
-> ssh-ed25519 NO562A /puQyDz+IepPqd8PTnD+YUp1obCh4gO1/7GGKaUAcRk
ydUvPiZyl9OZ3gVlhDxPkFf4PiPmQQtFoL9PK4pcA5Y
-> ssh-ed25519 5/zT0w MUEtkOeKzCBDBjoJjfkRnTmwl3pa0Qo07EKOpQp8RTQ
rUEyKc2BiQIxoPxTpoDPDfeJie2cfocwyT/VmIQcqL8
-> ssh-ed25519 d3WGuA 0pGUj+2YukJEgbIZ++iUKwfCp8yGiU2iUuyk0NFlYSg
/O3DY0EO3odBXvQNM14PdkrRmCLHOIvOIdgTsq5jBsQ
-> ssh-ed25519 6YMlxg Us2lOVb1NCc8yTqT/pe8UEb6qNePw7onN7J2z0mkb0s
WWtatJOs1krHZxmFddhmDOEfMNvGKBPFdAUnekKaccE
-> H7;a-ODO-grease <iF?
3z2aQlpJcCIhfAyzypdzUXfLZGGhYM7Rgp0uVOZYH7V1p/jspvJhrFyWKpcjfblj
qyjXRWASpQzl4jT5g1oUFB8u8A
--- lVvMLw/p/T07fWOaz/+IGn7uQgfb9BwXE8Wnh4F3JzA
äZÅî™å<¼ÔÝõŒ?EI­ãHÂç+*ïéM¹»œjk?/BÎâ4`ù7S^ t<>¨Ä U®`Soý1xjÒ®õ­z7D>ÄßñÀÜNy¾˜!`X@)ÿݱ?n6ú§(<ªãƒÀ|bG¨M…àµBd¼­3Ú§$±i#qåhðgP…j¸Þ¼äÒ…VT¯¸ :¡Å½Óõ¬và{Ïùâªò“‡ÿ´û?4Ï&0<>5O%D|u{<7B>ç*“2û¾„B!à'(.0Ö<EFBFBD>Ó°ßKg±Ñ¼ƒ"&LKq•<DÛòóÔqµ[;x²Pœ»]<5D>}ŽkÛëලXdfúÓïy
LÌC?†éhH(c¥I•¥<E280A2>½ÁyZÏ¿ãÐ|}#Å:w vìÇ5A¾ ÍzÈËrí÷:¥½› ÷

View file

@ -1,65 +0,0 @@
{ lib, tools, ... }:
# upstream's zone generator is pretty bad, so...
# TODO: make this prettier
let
inherit (tools.meta) domain;
inherit (tools) nginx identity;
externalSlave = { name, masters ? [ identity.dns.master.addr ], notify ? "no", alsoNotify ? [ "none" ] }: let
zoneName = "${name}";
file = "/var/named/slaves/ext_${zoneName}.db";
mastersFormatted = builtins.concatStringsSep "; " masters;
notifiersFormatted = builtins.concatStringsSep "; " alsoNotify;
in ''
zone "${zoneName}." IN {
type slave;
masters { ${mastersFormatted}; };
file "${file}";
allow-transfer { trusted; publicservers; };
allow-query { any; };
notify ${notify};
also-notify { ${notifiersFormatted}; };
};
'';
internalSlave' = domain: name: let
zoneName = "${name}${domain}";
file = "/var/named/slaves/int_${zoneName}.db";
in ''
zone "${zoneName}." IN {
type slave;
masters { ${identity.dns.master.addr}; };
file "${file}";
allow-transfer { trusted; };
allow-query { trusted; };
notify no;
};
'';
internalSlave = internalSlave' ".${domain}";
revSlave = internalSlave' ".in-addr.arpa";
toAttr = value: { inherit (value) name; inherit value; };
in
{
services.bind.extraConfig = builtins.concatStringsSep "\n" ([
(externalSlave { name = domain; notify = "explicit"; alsoNotify = [ "116.202.226.86" ]; })
(externalSlave { name = "imagine-using-oca.ml"; notify = "explicit"; alsoNotify = [ "116.202.226.86" ]; })
(externalSlave { name = "animus.com"; masters = [ "116.202.226.86" ]; })
] ++ map internalSlave [
"virtual-machines"
"core"
"services"
"ext"
"int"
"vpn"
"find"
] ++ map revSlave [
"0.10.10"
"1.10.10"
"2.10.10"
"100.10"
] ++ map (internalSlave' "") [
"void"
]);
}

View file

@ -19,7 +19,6 @@
./services/api
./services/backbone-routing
./services/bitwarden
./services/dns
./services/fbi
./services/gitlab
./services/hydra

View file

@ -4,6 +4,11 @@ let
systemKeys = x: x.ssh.id.publicKey or null;
in with hosts;
{
"cluster/services/dns/pdns-admin-oidc-secrets.age".publicKeys = max ++ map systemKeys [ VEGAS ];
"cluster/services/dns/pdns-admin-salt.age".publicKeys = max ++ map systemKeys [ VEGAS ];
"cluster/services/dns/pdns-admin-secret.age".publicKeys = max ++ map systemKeys [ VEGAS ];
"cluster/services/dns/pdns-api-key.age".publicKeys = max ++ map systemKeys [ VEGAS ];
"cluster/services/dns/pdns-db-credentials.age".publicKeys = max ++ map systemKeys [ VEGAS prophet ];
"cluster/services/patroni/passwords/replication.age".publicKeys = max ++ map systemKeys [ VEGAS prophet ];
"cluster/services/patroni/passwords/rewind.age".publicKeys = max ++ map systemKeys [ VEGAS prophet ];
"cluster/services/patroni/passwords/superuser.age".publicKeys = max ++ map systemKeys [ VEGAS prophet ];