nixos/home/routing-common: Add dynamic DNS update script
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				CI / Check, build and cache Nix flake (push) Successful in 20m48s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	CI / Check, build and cache Nix flake (push) Successful in 20m48s
				
			This commit is contained in:
		@@ -2,6 +2,7 @@ index: { lib, pkgs, config, assignments, allAssignments, ... }:
 | 
			
		||||
let
 | 
			
		||||
  inherit (builtins) attrNames elemAt;
 | 
			
		||||
  inherit (lib.my) net;
 | 
			
		||||
  inherit (lib.my.c) pubDomain;
 | 
			
		||||
  inherit (lib.my.c.home) prefixes vips routers;
 | 
			
		||||
 | 
			
		||||
  name = elemAt routers index;
 | 
			
		||||
@@ -22,6 +23,7 @@ in
 | 
			
		||||
          owner = "pdns-recursor";
 | 
			
		||||
          group = "pdns-recursor";
 | 
			
		||||
        };
 | 
			
		||||
        "home/ddclient-cloudflare.key" = {};
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      pdns.recursor = {
 | 
			
		||||
@@ -63,9 +65,36 @@ in
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    systemd.services = {
 | 
			
		||||
      # Add AF_NETLINK to allow pulling IP from network interfaces
 | 
			
		||||
      pdns.serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK";
 | 
			
		||||
    systemd = {
 | 
			
		||||
      services = {
 | 
			
		||||
        # Add AF_NETLINK to allow pulling IP from network interfaces
 | 
			
		||||
        pdns.serviceConfig.RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK";
 | 
			
		||||
        ddns-update = {
 | 
			
		||||
          description = "DNS update script";
 | 
			
		||||
          after = [ "network.target" ];
 | 
			
		||||
          path = [
 | 
			
		||||
            (pkgs.python3.withPackages (ps: [ ps.cloudflare ]))
 | 
			
		||||
            pkgs.ldns
 | 
			
		||||
          ];
 | 
			
		||||
          serviceConfig = {
 | 
			
		||||
            Type = "oneshot";
 | 
			
		||||
            ExecStart =
 | 
			
		||||
              ''${./dns_update.py} -k ${config.age.secrets."home/ddclient-cloudflare.key".path} '' +
 | 
			
		||||
              ''${pubDomain} ns${toString (index + 1)}.${config.networking.domain}'';
 | 
			
		||||
          };
 | 
			
		||||
          wantedBy = [ "multi-user.target" ];
 | 
			
		||||
        };
 | 
			
		||||
      };
 | 
			
		||||
      timers = {
 | 
			
		||||
        ddns-update = {
 | 
			
		||||
          description = "Periodically update DNS";
 | 
			
		||||
          wantedBy = [ "timers.target" ];
 | 
			
		||||
          timerConfig = {
 | 
			
		||||
            OnBootSec = "5min";
 | 
			
		||||
            OnUnitInactiveSec = "5min";
 | 
			
		||||
          };
 | 
			
		||||
        };
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    environment.systemPackages = with pkgs; [
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								nixos/boxes/home/routing-common/dns_update.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										35
									
								
								nixos/boxes/home/routing-common/dns_update.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
import argparse
 | 
			
		||||
import subprocess
 | 
			
		||||
 | 
			
		||||
import CloudFlare
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    parser = argparse.ArgumentParser(description='Cloudflare DNS update script')
 | 
			
		||||
    parser.add_argument('-k', '--api-token-file', help='Cloudflare API token file')
 | 
			
		||||
    parser.add_argument('zone', help='Cloudflare Zone')
 | 
			
		||||
    parser.add_argument('record', help='Cloudflare record name')
 | 
			
		||||
    args = parser.parse_args()
 | 
			
		||||
 | 
			
		||||
    address = subprocess.check_output(
 | 
			
		||||
        ['drill', '-Q', '-p5353', '@127.0.0.1', args.record, 'A'],
 | 
			
		||||
        encoding='utf8').strip()
 | 
			
		||||
 | 
			
		||||
    cf_token = None
 | 
			
		||||
    if args.api_token_file:
 | 
			
		||||
        with open(args.api_token_file) as f:
 | 
			
		||||
            cf_token = f.readline().strip()
 | 
			
		||||
 | 
			
		||||
    cf = CloudFlare.CloudFlare(token=cf_token)
 | 
			
		||||
    zones = cf.zones.get(params={'name': args.zone})
 | 
			
		||||
    assert zones, f'Zone {args.zone} not found'
 | 
			
		||||
    records = cf.zones.dns_records.get(zones[0]['id'], params={'name': args.record})
 | 
			
		||||
    assert records, f'Record {args.record} not found in zone {args.zone}'
 | 
			
		||||
 | 
			
		||||
    print(f'Updating {args.record} -> {address}')
 | 
			
		||||
    cf.zones.dns_records.patch(
 | 
			
		||||
        zones[0]['id'], records[0]['id'],
 | 
			
		||||
        data={'type': 'A', 'name': args.record, 'content': address})
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    main()
 | 
			
		||||
		Reference in New Issue
	
	Block a user