setup octodns for automatic dns records
This commit is contained in:
@@ -3,7 +3,10 @@
|
|||||||
# and in the NixOS manual (accessible by running `nixos-help`).
|
# and in the NixOS manual (accessible by running `nixos-help`).
|
||||||
|
|
||||||
{ config, pkgs, ssh, ... }:
|
{ config, pkgs, ssh, ... }:
|
||||||
|
let
|
||||||
|
ipv4Address = "65.21.145.150";
|
||||||
|
ipv6Address = "2a01:4f9:c011:9ac1::1";
|
||||||
|
in
|
||||||
{
|
{
|
||||||
nix = {
|
nix = {
|
||||||
settings = {
|
settings = {
|
||||||
@@ -25,13 +28,24 @@
|
|||||||
networking.useDHCP = false;
|
networking.useDHCP = false;
|
||||||
networking.nftables.enable = true;
|
networking.nftables.enable = true;
|
||||||
|
|
||||||
|
services.octodns.records."" = {
|
||||||
|
A = {
|
||||||
|
ttl = 86400;
|
||||||
|
values = [ ipv4Address ];
|
||||||
|
};
|
||||||
|
AAAA = {
|
||||||
|
ttl = 86400;
|
||||||
|
values = [ ipv6Address ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
systemd.network = {
|
systemd.network = {
|
||||||
enable = true;
|
enable = true;
|
||||||
networks.static = {
|
networks.static = {
|
||||||
name = "enp1s0";
|
name = "enp1s0";
|
||||||
address = [
|
address = [
|
||||||
"65.21.145.150/32"
|
"${ipv4Address}/32"
|
||||||
"2a01:4f9:c011:9ac1::1/64"
|
"${ipv6Address}/64"
|
||||||
];
|
];
|
||||||
routes = [
|
routes = [
|
||||||
{ Gateway = "fe80::1"; }
|
{ Gateway = "fe80::1"; }
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
"vaultwarden"
|
"vaultwarden"
|
||||||
"donetick"
|
"donetick"
|
||||||
"dnote"
|
"dnote"
|
||||||
|
"octodns"
|
||||||
]
|
]
|
||||||
) // {
|
) // {
|
||||||
smtp-password = {
|
smtp-password = {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ let
|
|||||||
secrets = config.age.secrets;
|
secrets = config.age.secrets;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
services.postgresql.package = pkgs.postgresql_16;
|
|
||||||
virtualisation.podman.enable = true;
|
virtualisation.podman.enable = true;
|
||||||
virtualisation.oci-containers.backend = "podman";
|
virtualisation.oci-containers.backend = "podman";
|
||||||
|
|
||||||
@@ -33,6 +32,16 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
services = {
|
services = {
|
||||||
|
postgresql.package = pkgs.postgresql_16;
|
||||||
|
octodns = {
|
||||||
|
enable = true;
|
||||||
|
records."".MX = {
|
||||||
|
ttl = 86400;
|
||||||
|
values = [{ exchange = "${smtp.host}."; }];
|
||||||
|
};
|
||||||
|
defaults.CNAME.ttl = 60;
|
||||||
|
};
|
||||||
|
|
||||||
hastebin = {
|
hastebin = {
|
||||||
enable = true;
|
enable = true;
|
||||||
subdomain = "bin";
|
subdomain = "bin";
|
||||||
|
|||||||
@@ -17,5 +17,6 @@
|
|||||||
./readeck.nix
|
./readeck.nix
|
||||||
./donetick.nix
|
./donetick.nix
|
||||||
./dnote.nix
|
./dnote.nix
|
||||||
|
./octodns.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,28 +31,30 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services.prometheus = {
|
services = {
|
||||||
enable = true;
|
prometheus = {
|
||||||
exporters.node.enable = true;
|
enable = true;
|
||||||
scrapeConfigs = [
|
exporters.node.enable = true;
|
||||||
{
|
scrapeConfigs = [
|
||||||
job_name = "node";
|
{
|
||||||
static_configs = [
|
job_name = "node";
|
||||||
{ targets = [ "localhost:9100" ]; }
|
static_configs = [
|
||||||
];
|
{ targets = [ "localhost:9100" ]; }
|
||||||
}
|
];
|
||||||
];
|
}
|
||||||
};
|
];
|
||||||
|
};
|
||||||
|
|
||||||
services.webserver.vHosts.${fqdn}.locations."/".proxyPort = cfg.settings.server.http_port;
|
webserver.vHosts.${fqdn}.locations."/".proxyPort = cfg.settings.server.http_port;
|
||||||
|
|
||||||
services.postgresql = {
|
postgresql = {
|
||||||
enable = lib.mkDefault true;
|
enable = lib.mkDefault true;
|
||||||
ensureDatabases = [ "grafana" ];
|
ensureDatabases = [ "grafana" ];
|
||||||
ensureUsers = [{
|
ensureUsers = [{
|
||||||
name = "grafana";
|
name = "grafana";
|
||||||
ensureDBOwnership = true;
|
ensureDBOwnership = true;
|
||||||
}];
|
}];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,24 +11,26 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
services.invidious = {
|
services = {
|
||||||
domain = fqdn;
|
invidious = {
|
||||||
address = "127.0.0.1";
|
domain = fqdn;
|
||||||
|
address = "127.0.0.1";
|
||||||
|
|
||||||
settings = {
|
settings = {
|
||||||
external_port = 443;
|
external_port = 443;
|
||||||
db = {
|
db = {
|
||||||
dbname = "invidious";
|
dbname = "invidious";
|
||||||
user = "invidious";
|
user = "invidious";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
services.postgresql.enable = lib.mkDefault true;
|
postgresql.enable = lib.mkDefault true;
|
||||||
|
|
||||||
services.webserver = {
|
webserver = {
|
||||||
enable = lib.mkDefault true;
|
enable = lib.mkDefault true;
|
||||||
vHosts.${fqdn}.locations."/".proxyPort = cfg.port;
|
vHosts.${fqdn}.locations."/".proxyPort = cfg.port;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
210
modules/services/octodns.nix
Normal file
210
modules/services/octodns.nix
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
{ pkgs, lib, config, ... }:
|
||||||
|
let
|
||||||
|
cfg = config.services.octodns;
|
||||||
|
secrets = config.age.secrets;
|
||||||
|
|
||||||
|
types = {
|
||||||
|
ttlOptions = default: {
|
||||||
|
ttl = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
default = default;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
defaults = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
A = lib.mkOption {
|
||||||
|
type = lib.types.submodule {
|
||||||
|
options = types.ttlOptions cfg.defaults.ttl;
|
||||||
|
};
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
AAAA = lib.mkOption {
|
||||||
|
type = lib.types.submodule {
|
||||||
|
options = types.ttlOptions cfg.defaults.ttl;
|
||||||
|
};
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
CNAME = lib.mkOption {
|
||||||
|
type = lib.types.submodule {
|
||||||
|
options = types.ttlOptions cfg.defaults.ttl;
|
||||||
|
};
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
MX = lib.mkOption {
|
||||||
|
type = lib.types.submodule {
|
||||||
|
options = types.ttlOptions cfg.defaults.ttl;
|
||||||
|
};
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
} // (types.ttlOptions 3600);
|
||||||
|
};
|
||||||
|
aRecord = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
values = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
ttl = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
default = cfg.defaults.A.ttl;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
aaaaRecord = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
values = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
ttl = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
default = cfg.defaults.AAAA.ttl;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
cnameRecord = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
target = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
toRoot = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
ttl = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
default = cfg.defaults.CNAME.ttl;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
mxValue = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
exchange = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
};
|
||||||
|
preference = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
default = 10;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
mxRecord = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
values = lib.mkOption {
|
||||||
|
type = lib.types.listOf types.mxValue;
|
||||||
|
default = [ ];
|
||||||
|
};
|
||||||
|
ttl = lib.mkOption {
|
||||||
|
type = lib.types.int;
|
||||||
|
default = cfg.defaults.MX.ttl;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
records = lib.types.submodule {
|
||||||
|
options = {
|
||||||
|
A = lib.mkOption {
|
||||||
|
type = types.aRecord;
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
AAAA = lib.mkOption {
|
||||||
|
type = types.aaaaRecord;
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
CNAME = lib.mkOption {
|
||||||
|
type = types.cnameRecord;
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
MX = lib.mkOption {
|
||||||
|
type = types.mxRecord;
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
yamlFormat = pkgs.formats.yaml { };
|
||||||
|
|
||||||
|
zoneFile = yamlFormat.generate "octodns-zone"
|
||||||
|
(
|
||||||
|
lib.filterAttrs (_: records: (lib.length records) > 0)
|
||||||
|
(lib.mapAttrs
|
||||||
|
(_: types:
|
||||||
|
lib.filter
|
||||||
|
({ values ? [ ], value ? null, ... }: (lib.length values) > 0 || !(builtins.isNull value))
|
||||||
|
(lib.mapAttrsToList
|
||||||
|
(type: { ttl, ... }@options:
|
||||||
|
if (type == "CNAME")
|
||||||
|
then
|
||||||
|
let
|
||||||
|
inherit (options) target toRoot;
|
||||||
|
value = if toRoot then "${config.networking.domain}." else target;
|
||||||
|
in
|
||||||
|
{ inherit type ttl value; }
|
||||||
|
else
|
||||||
|
{ inherit type ttl; inherit (options) values; }
|
||||||
|
)
|
||||||
|
types
|
||||||
|
)
|
||||||
|
)
|
||||||
|
cfg.records
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
zonesDir = pkgs.linkFarm "octodns-zones" {
|
||||||
|
"${config.networking.domain}.yaml" = zoneFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
configFile = yamlFormat.generate "octodns-config.yaml" {
|
||||||
|
providers = {
|
||||||
|
config = {
|
||||||
|
class = "octodns.provider.yaml.YamlProvider";
|
||||||
|
directory = zonesDir;
|
||||||
|
default_ttl = cfg.defaults.ttl;
|
||||||
|
};
|
||||||
|
hetzner = {
|
||||||
|
class = "octodns_hetzner.HetznerProvider";
|
||||||
|
token = "env/HETZNER_TOKEN";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
zones."*" = {
|
||||||
|
sources = [ "config" ];
|
||||||
|
targets = [ "hetzner" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
octodns = pkgs.octodns.withProviders (_: [
|
||||||
|
pkgs.octodns-providers.hetzner
|
||||||
|
]);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.octodns = {
|
||||||
|
enable = lib.mkEnableOption "Enable octodns";
|
||||||
|
records = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf types.records;
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
defaults = lib.mkOption {
|
||||||
|
type = types.defaults;
|
||||||
|
default = { };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
systemd.services.octodns = {
|
||||||
|
enable = true;
|
||||||
|
description = "OctoDNS";
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = "${octodns}/bin/octodns-sync --config-file ${configFile} --doit";
|
||||||
|
DynamicUser = true;
|
||||||
|
EnvironmentFile = secrets.octodns.path;
|
||||||
|
RemainAfterExit = true;
|
||||||
|
};
|
||||||
|
wantedBy = lib.optionals config.services.nginx.enable [ "nginx.service" ];
|
||||||
|
before = lib.optionals config.services.nginx.enable [ "nginx.service" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -11,10 +11,12 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
services.owncast = {
|
services = {
|
||||||
openFirewall = true;
|
owncast = {
|
||||||
};
|
openFirewall = true;
|
||||||
|
};
|
||||||
|
|
||||||
services.webserver.vHosts.${fqdn}.locations."/".proxyPort = cfg.port;
|
webserver.vHosts.${fqdn}.locations."/".proxyPort = cfg.port;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,15 +15,17 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
services.syncthing = {
|
services = {
|
||||||
openDefaultPorts = true;
|
syncthing = {
|
||||||
guiAddress = "[::1]:${toString cfg.port}";
|
openDefaultPorts = true;
|
||||||
settings.gui.insecureSkipHostCheck = true;
|
guiAddress = "[::1]:${toString cfg.port}";
|
||||||
};
|
settings.gui.insecureSkipHostCheck = true;
|
||||||
|
};
|
||||||
|
|
||||||
services.webserver = {
|
webserver = {
|
||||||
enable = lib.mkDefault true;
|
enable = lib.mkDefault true;
|
||||||
vHosts.${fqdn}.locations."/".proxyPort = cfg.port;
|
vHosts.${fqdn}.locations."/".proxyPort = cfg.port;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,36 +46,47 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
services.nginx = {
|
services = {
|
||||||
enable = lib.mkDefault true;
|
nginx = {
|
||||||
recommendedProxySettings = true;
|
enable = lib.mkDefault true;
|
||||||
recommendedTlsSettings = true;
|
recommendedProxySettings = true;
|
||||||
recommendedBrotliSettings = true;
|
recommendedTlsSettings = true;
|
||||||
recommendedGzipSettings = true;
|
recommendedBrotliSettings = true;
|
||||||
recommendedZstdSettings = true;
|
recommendedGzipSettings = true;
|
||||||
recommendedOptimisation = true;
|
recommendedZstdSettings = true;
|
||||||
|
recommendedOptimisation = true;
|
||||||
|
|
||||||
virtualHosts = lib.mapAttrs
|
virtualHosts = lib.mapAttrs
|
||||||
(_: { proxyBuffering, locations }: {
|
(_: { proxyBuffering, locations }: {
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
http2 = true;
|
http2 = true;
|
||||||
acmeRoot = lib.mkIf cfg.acme.dnsChallenge null;
|
acmeRoot = lib.mkIf cfg.acme.dnsChallenge null;
|
||||||
extraConfig = lib.concatLines [
|
extraConfig = lib.concatLines [
|
||||||
(lib.optionalString (!proxyBuffering) "proxy_buffering off;")
|
(lib.optionalString (!proxyBuffering) "proxy_buffering off;")
|
||||||
"charset utf-8;"
|
"charset utf-8;"
|
||||||
];
|
];
|
||||||
locations = lib.mapAttrs
|
locations = lib.mapAttrs
|
||||||
(_: { proxyPort, extraConfig }: lib.mergeAttrsList [
|
(_: { proxyPort, extraConfig }: lib.mergeAttrsList [
|
||||||
{ inherit extraConfig; }
|
{ inherit extraConfig; }
|
||||||
(if (lib.isInt proxyPort) then {
|
(if (lib.isInt proxyPort) then {
|
||||||
proxyWebsockets = true;
|
proxyWebsockets = true;
|
||||||
proxyPass = "http://localhost:${toString proxyPort}";
|
proxyPass = "http://localhost:${toString proxyPort}";
|
||||||
} else { })
|
} else { })
|
||||||
])
|
])
|
||||||
locations;
|
locations;
|
||||||
})
|
})
|
||||||
cfg.vHosts;
|
cfg.vHosts;
|
||||||
|
};
|
||||||
|
|
||||||
|
octodns.records = lib.filterAttrs (name: _: name != config.networking.domain) (
|
||||||
|
lib.mapAttrs'
|
||||||
|
(fqdn: _: {
|
||||||
|
name = lib.removeSuffix ".${config.networking.domain}" fqdn;
|
||||||
|
value = { CNAME.toRoot = true; };
|
||||||
|
})
|
||||||
|
cfg.vHosts
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
security.acme = {
|
security.acme = {
|
||||||
|
|||||||
@@ -46,16 +46,18 @@ in
|
|||||||
confinement.enable = true;
|
confinement.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
services.webserver = {
|
services = {
|
||||||
enable = lib.mkDefault true;
|
webserver = {
|
||||||
vHosts.${fqdn}.locations."/" = {
|
enable = lib.mkDefault true;
|
||||||
proxyPort = port;
|
vHosts.${fqdn}.locations."/" = {
|
||||||
extraConfig = ''
|
proxyPort = port;
|
||||||
client_max_body_size 50m;
|
extraConfig = ''
|
||||||
proxy_send_timeout 300;
|
client_max_body_size 50m;
|
||||||
proxy_read_timeout 300;
|
proxy_send_timeout 300;
|
||||||
send_timeout 300;
|
proxy_read_timeout 300;
|
||||||
'';
|
send_timeout 300;
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,11 +13,13 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
services.workout-tracker = { inherit package; };
|
services = {
|
||||||
|
workout-tracker = { inherit package; };
|
||||||
|
|
||||||
services.webserver = {
|
webserver = {
|
||||||
enable = lib.mkDefault true;
|
enable = lib.mkDefault true;
|
||||||
vHosts.${fqdn}.locations."/".proxyPort = port;
|
vHosts.${fqdn}.locations."/".proxyPort = port;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
secrets/octodns.age
Normal file
BIN
secrets/octodns.age
Normal file
Binary file not shown.
@@ -19,4 +19,5 @@ in
|
|||||||
"readeck.age".publicKeys = users ++ [ freun-dev ];
|
"readeck.age".publicKeys = users ++ [ freun-dev ];
|
||||||
"donetick.age".publicKeys = users ++ [ freun-dev ];
|
"donetick.age".publicKeys = users ++ [ freun-dev ];
|
||||||
"dnote.age".publicKeys = users ++ [ freun-dev ];
|
"dnote.age".publicKeys = users ++ [ freun-dev ];
|
||||||
|
"octodns.age".publicKeys = users ++ [ freun-dev ];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user