From 01a27e45ceb2502fcb025e3dc7b733f93a78f3da Mon Sep 17 00:00:00 2001 From: Timotej Lazar Date: Fri, 2 Aug 2024 12:08:32 +0200 Subject: [PATCH 1/2] dnsmasq: add script for dynamic DNS updates --- roles/dnsmasq/tasks/main.yml | 27 +++++++++++++++++++- roles/dnsmasq/templates/00-options.conf.j2 | 2 ++ roles/dnsmasq/templates/dns-update.j2 | 29 ++++++++++++++++++++++ roles/dnsmasq/templates/krb5.conf.j2 | 18 ++++++++++++++ 4 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 roles/dnsmasq/templates/dns-update.j2 create mode 100644 roles/dnsmasq/templates/krb5.conf.j2 diff --git a/roles/dnsmasq/tasks/main.yml b/roles/dnsmasq/tasks/main.yml index fb7b0a3..81a8c66 100644 --- a/roles/dnsmasq/tasks/main.yml +++ b/roles/dnsmasq/tasks/main.yml @@ -2,6 +2,32 @@ package: name: - dnsmasq + - bind-tools + - krb5 + - py3-pexpect + +- name: Configure kerberos + template: + dest: /etc/krb5.conf + src: krb5.conf.j2 + +- name: Init kerberos keytab + expect: + command: ktutil + responses: + '.*:': + - 'add_entry -password -p {{ password.ldap_user }} -k 1 -e aes256-cts-hmac-sha1-96' + - '{{ password.ldap_pass }}' + - 'write_kt /etc/krb5.keytab' + - 'exit' + args: + creates: /etc/krb5.keytab + +- name: Copy DNS updater script + template: + dest: "/usr/local/bin/dns-update" + src: "dns-update.j2" + mode: 0700 - name: Configure dnsmasq template: @@ -12,5 +38,4 @@ - 10-ranges.conf notify: restart dnsmasq -# TODO DNS update # TODO netboot config diff --git a/roles/dnsmasq/templates/00-options.conf.j2 b/roles/dnsmasq/templates/00-options.conf.j2 index 0f5bd43..6e507e7 100644 --- a/roles/dnsmasq/templates/00-options.conf.j2 +++ b/roles/dnsmasq/templates/00-options.conf.j2 @@ -9,3 +9,5 @@ dhcp-proxy dhcp-option = option:dns-server,{{ dns | join(',') }} dhcp-option = option:ntp-server,{{ ntp | join(',') }} + +dhcp-script = /usr/local/bin/dns-update diff --git a/roles/dnsmasq/templates/dns-update.j2 b/roles/dnsmasq/templates/dns-update.j2 new file mode 100644 index 0000000..69b0351 --- /dev/null +++ b/roles/dnsmasq/templates/dns-update.j2 @@ -0,0 +1,29 @@ +#!/bin/sh + +domain={{ domain }} +ldap_user={{ password.ldap_user }} +ttl=3600 + +address="${3}" + +case "${1}" in +add) + host="${4}" + kinit -k "${ldap_user}" + nsupdate -g < Date: Sat, 3 Aug 2024 11:44:03 +0200 Subject: [PATCH 2/2] Support custom allowed_ips field for services Like allowed_prefixes, but for single IP addresses. Currently used just for DHCP server to allow (only) packets from relays. --- filter_plugins/netbox.py | 16 +++++++++++++--- roles/alpine/templates/local.nft.j2 | 12 +++++------- roles/ceph/templates/nftables.conf.j2 | 10 ++++------ roles/proxmox/templates/cluster.fw.j2 | 10 ++++------ 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/filter_plugins/netbox.py b/filter_plugins/netbox.py index fac0a93..99dc67d 100644 --- a/filter_plugins/netbox.py +++ b/filter_plugins/netbox.py @@ -1,5 +1,6 @@ #!/usr/bin/python +import ipaddress import os import pynetbox @@ -38,12 +39,21 @@ class FilterModule(object): def allowed_prefixes(self, service): '''Return a list of allowed IP prefixes for the given service''' + # Depending on how service was obtained, actual data might be nested under custom_fields. if 'custom_fields' in service: service = service['custom_fields'] + + def ip2str(address, single=False): + if single: # 1.2.3.4/24 → 1.2.3.4 + address = ipaddress.ip_interface(address).ip + return str(ipaddress.ip_interface(address)) + + if ips := service.get('allowed_ips'): + yield from (ip2str(ip['address'], single=True) for ip in ips) if prefixes := service.get('allowed_prefixes'): - yield from self.nb.ipam.prefixes.filter(id=[prefix['id'] for prefix in prefixes]) + yield from (ip2str(e.prefix) for e in self.nb.ipam.prefixes.filter(id=[prefix['id'] for prefix in prefixes])) if vlans := service.get('allowed_vlans'): - yield from self.nb.ipam.prefixes.filter(vlan_id=[vlan['id'] for vlan in vlans]) + yield from (ip2str(e.prefix) for e in self.nb.ipam.prefixes.filter(vlan_id=[vlan['id'] for vlan in vlans])) if clusters := service.get('allowed_clusters'): for device in self.nb.dcim.devices.filter(cluster_id=[cluster['id'] for cluster in clusters]): - yield from self.nb.ipam.ip_addresses.filter(role='loopback', device_id=device.id) + yield from (ip2str(e.address) for e in self.nb.ipam.ip_addresses.filter(role='loopback', device_id=device.id)) diff --git a/roles/alpine/templates/local.nft.j2 b/roles/alpine/templates/local.nft.j2 index 484a8e8..73573e4 100644 --- a/roles/alpine/templates/local.nft.j2 +++ b/roles/alpine/templates/local.nft.j2 @@ -4,18 +4,16 @@ table inet filter { {% for service in services %} {% set prefixes = service | allowed_prefixes %} -{% set prefixes4 = prefixes | selectattr('family.value', '==', 4) | map('string') %} -{% set prefixes6 = prefixes | selectattr('family.value', '==', 6) | map('string') %} {% set ports = service.ports | compact_numlist %} {% if 'name' in service %} # service {{ service.name }} {% endif %} -{% if prefixes4 or prefixes6 %} -{% if prefixes4 %} - ip saddr { {{ prefixes4 | join(', ') }} } {{ service.protocol.value }} dport { {{ ports }} } accept +{% if prefixes %} +{% if prefixes | ipv4 %} + ip saddr { {{ prefixes | ipv4 | join(', ') }} } {{ service.protocol.value }} dport { {{ ports }} } accept {% endif %} -{% if prefixes6 %} - ip6 saddr { {{ prefixes6 | join(', ') }} } {{ service.protocol.value }} dport { {{ ports }} } accept +{% if prefixes | ipv6 %} + ip6 saddr { {{ prefixes | ipv6 | join(', ') }} } {{ service.protocol.value }} dport { {{ ports }} } accept {% endif %} {% else %} tcp dport { {{ ports }} } accept diff --git a/roles/ceph/templates/nftables.conf.j2 b/roles/ceph/templates/nftables.conf.j2 index be3e9ce..e50c6f6 100644 --- a/roles/ceph/templates/nftables.conf.j2 +++ b/roles/ceph/templates/nftables.conf.j2 @@ -56,15 +56,13 @@ table inet filter { {% for service in cluster_services %} {% set prefixes = service | allowed_prefixes %} -{% set prefixes4 = prefixes | selectattr('family.value', '==', 4) | map('string') %} -{% set prefixes6 = prefixes | selectattr('family.value', '==', 6) | map('string') %} {% set ports = service.ports | compact_numlist %} # service {{ service.name }} -{% if prefixes4 %} - ip saddr { {{ prefixes4 | join(', ') }} } tcp dport { {{ ports }} } accept +{% if prefixes | ipv4 %} + ip saddr { {{ prefixes | ipv4 | join(', ') }} } tcp dport { {{ ports }} } accept {% endif %} -{% if prefixes6 %} - ip6 saddr { {{ prefixes6 | join(', ') }} } tcp dport { {{ ports }} } accept +{% if prefixes | ipv6 %} + ip6 saddr { {{ prefixes | ipv6 | join(', ') }} } tcp dport { {{ ports }} } accept {% endif %} {% endfor %} diff --git a/roles/proxmox/templates/cluster.fw.j2 b/roles/proxmox/templates/cluster.fw.j2 index 734d1e9..a5981ba 100644 --- a/roles/proxmox/templates/cluster.fw.j2 +++ b/roles/proxmox/templates/cluster.fw.j2 @@ -10,14 +10,12 @@ IN ACCEPT -source {{ nodes | map('device_address') | flatten | selectattr('famil IN ACCEPT -source {{ nodes | map('device_address') | flatten | selectattr('family.value', '==', 6) | map(attribute='address') | join(',') }} # my cluster {% for service in cluster_services %} {% set prefixes = service | allowed_prefixes %} -{% set prefixes4 = prefixes | selectattr('family.value', '==', 4) | map('string') %} -{% set prefixes6 = prefixes | selectattr('family.value', '==', 6) | map('string') %} {% set ports = service.ports | compact_numlist(range_delimiter=':') %} -{% if prefixes4 %} -IN ACCEPT -source {{ prefixes4 | join(',') }} -p {{ service.protocol.value }} -dport {{ ports }} # {{ service.name }} +{% if prefixes | ipv4 %} +IN ACCEPT -source {{ prefixes | ipv4 | join(',') }} -p {{ service.protocol.value }} -dport {{ ports }} # {{ service.name }} {% endif %} -{% if prefixes6 %} -IN ACCEPT -source {{ prefixes6 | join(',') }} -p {{ service.protocol.value }} -dport {{ ports }} # {{ service.name }} +{% if prefixes | ipv6 %} +IN ACCEPT -source {{ prefixes | ipv6 | join(',') }} -p {{ service.protocol.value }} -dport {{ ports }} # {{ service.name }} {% endif %} {% endfor %}