apu: setup dynamic dns record based on DHCP leases
This commit is contained in:
130
modules/services/dhcp-dns-sync/default.nix
Normal file
130
modules/services/dhcp-dns-sync/default.nix
Normal file
@@ -0,0 +1,130 @@
|
||||
{
|
||||
lib,
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.modules.services.dhcp-dns-sync;
|
||||
|
||||
dhcp-leases-to-unbound =
|
||||
pkgs.runCommand "dhcp-leases-to-unbound"
|
||||
{
|
||||
code = ./dhcp-leases-to-unbound.cr;
|
||||
nativeBuildInputs = [ pkgs.crystal ];
|
||||
meta.mainProgram = "dhcp-leases-to-unbound";
|
||||
}
|
||||
''
|
||||
mkdir -p $out/bin
|
||||
crystal build $code --release -o $out/bin/dhcp-leases-to-unbound
|
||||
'';
|
||||
in
|
||||
{
|
||||
options.modules.services.dhcp-dns-sync = {
|
||||
enable = lib.mkEnableOption "Enable DHCP to DNS synchronization";
|
||||
|
||||
interface = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "koti";
|
||||
description = "Network interface to monitor for DHCP leases";
|
||||
};
|
||||
|
||||
domain = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "home.arpa";
|
||||
description = "Domain suffix for DHCP hostnames";
|
||||
};
|
||||
|
||||
unboundConfigPath = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/var/lib/unbound/dhcp-hosts.conf";
|
||||
description = "Path to write Unbound include file";
|
||||
};
|
||||
|
||||
leasesJsonPath = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "/var/lib/router/leases.json";
|
||||
description = "Path to write leases JSON file";
|
||||
};
|
||||
|
||||
interval = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "30s";
|
||||
description = "Interval for checking DHCP lease updates";
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
# Create user and group for the service
|
||||
users.users.dhcp-dns-sync = {
|
||||
isSystemUser = true;
|
||||
group = "dhcp-dns-sync";
|
||||
description = "DHCP DNS sync service user";
|
||||
};
|
||||
|
||||
users.groups.dhcp-dns-sync = { };
|
||||
|
||||
# Ensure directories and files exist with proper permissions
|
||||
systemd.tmpfiles.rules = [
|
||||
"d /var/lib/unbound 0755 unbound unbound -"
|
||||
"d /var/lib/router 0755 dhcp-dns-sync dhcp-dns-sync -"
|
||||
"f ${cfg.unboundConfigPath} 0644 dhcp-dns-sync dhcp-dns-sync -"
|
||||
];
|
||||
|
||||
# Extend Unbound configuration to include generated file
|
||||
services.unbound.settings = {
|
||||
server = {
|
||||
local-zone = [ "${cfg.domain}. static" ];
|
||||
include = cfg.unboundConfigPath;
|
||||
};
|
||||
};
|
||||
|
||||
# Make sure Unbound control is enabled
|
||||
services.unbound.settings.remote-control.control-enable = true;
|
||||
|
||||
# Systemd service
|
||||
systemd.services.dhcp-dns-sync = {
|
||||
description = "Sync DHCP leases to Unbound DNS";
|
||||
after = [
|
||||
"systemd-networkd.service"
|
||||
"unbound.service"
|
||||
];
|
||||
requires = [ "unbound.service" ];
|
||||
wants = [ "unbound-control.socket" ];
|
||||
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = "dhcp-dns-sync";
|
||||
Group = "dhcp-dns-sync";
|
||||
# Allow access to networkctl via D-Bus
|
||||
SupplementaryGroups = [ "systemd-network" ];
|
||||
# Read/write paths
|
||||
ReadWritePaths = [
|
||||
"/var/lib/unbound"
|
||||
"/var/lib/router"
|
||||
];
|
||||
# Execute paths
|
||||
ExecPaths = [ "/run/current-system/sw/bin" ];
|
||||
};
|
||||
|
||||
script = ''
|
||||
${lib.getExe dhcp-leases-to-unbound} \
|
||||
-i ${cfg.interface} \
|
||||
-d ${cfg.domain} \
|
||||
-o ${cfg.unboundConfigPath} \
|
||||
--leases-json ${cfg.leasesJsonPath} \
|
||||
--networkctl ${lib.getExe' pkgs.systemd "networkctl"}
|
||||
'';
|
||||
};
|
||||
|
||||
# Systemd timer
|
||||
systemd.timers.dhcp-dns-sync = {
|
||||
description = "Periodic DHCP to DNS sync";
|
||||
wantedBy = [ "timers.target" ];
|
||||
timerConfig = {
|
||||
OnBootSec = "10s";
|
||||
OnUnitActiveSec = cfg.interval;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user