setup octodns for automatic dns records

This commit is contained in:
Joakim Repomaa
2025-02-15 03:22:20 +02:00
parent 459cae639d
commit c15d518e4a
14 changed files with 351 additions and 92 deletions

View 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" ];
};
};
}