diff --git a/filter_plugins/netbox.py b/filter_plugins/netbox.py index db0c629..b519b3f 100644 --- a/filter_plugins/netbox.py +++ b/filter_plugins/netbox.py @@ -1,10 +1,19 @@ #!/usr/bin/python +import os +import pynetbox + class FilterModule(object): '''Various utilities for manipulating NetBox data''' + + def __init__(self): + self.nb = pynetbox.api(os.getenv('NETBOX_API'), os.getenv('NETBOX_TOKEN')) + def filters(self): return { - 'device_address': self.device_address + 'device_address': self.device_address, + 'compact_numlist': self.compact_numlist, + 'allowed_prefixes': self.allowed_prefixes } def device_address(self, device): @@ -13,3 +22,26 @@ class FilterModule(object): for addr in iface['ip_addresses']: if addr.get('role') and addr['role'].get('value') == 'loopback': yield addr + + def compact_numlist(self, nums, delimiter=',', range_delimiter='-'): + '''Transform [1,2,3,5,7,8,9] into "1-3,5,7-9"''' + i = 0 + spans = [] + while i < len(nums): + j = i + 1 + while j < len(nums) and nums[j]-nums[i] == j-i: + j += 1 + spans += [f'{nums[i]}{range_delimiter}{nums[j-1]}' if j > i+1 else f'{nums[i]}'] + i = j + return delimiter.join(spans) + + def allowed_prefixes(self, service): + '''Return a list of allowed IP prefixes for the given service''' + service_data = self.nb.ipam.services.get(service['id']).custom_fields + if service_data['allowed_prefixes']: + yield from self.nb.ipam.prefixes.filter(id=[prefix['id'] for prefix in service_data['allowed_prefixes']]) + if service_data['allowed_vlans']: + yield from self.nb.ipam.prefixes.filter(vlan_id=[vlan['id'] for vlan in service_data['allowed_vlans']]) + if service_data['allowed_clusters']: + for device in self.nb.dcim.devices.filter(cluster_id=[cluster['id'] for cluster in service_data['allowed_clusters']]): + yield from self.nb.ipam.ip_addresses.filter(role='loopback', device_id=device.id) diff --git a/roles/proxmox/tasks/firewall.yml b/roles/proxmox/tasks/firewall.yml new file mode 100644 index 0000000..32c3328 --- /dev/null +++ b/roles/proxmox/tasks/firewall.yml @@ -0,0 +1,12 @@ +- name: Retrieve service list + set_fact: + services: '{{ query("netbox.netbox.nb_lookup", "clusters", raw_data=true, api_filter="name="+cluster) | map(attribute="custom_fields.services") | flatten }}' + +- name: Set up firewall + template: + dest: /etc/pve/firewall/cluster.fw + src: cluster.fw.j2 + mode: 0640 + owner: root + group: www-data + diff --git a/roles/proxmox/tasks/main.yml b/roles/proxmox/tasks/main.yml index 9007844..e2460ef 100644 --- a/roles/proxmox/tasks/main.yml +++ b/roles/proxmox/tasks/main.yml @@ -1,3 +1,7 @@ +- name: Get all nodes in my cluster + set_fact: + nodes: "{{ groups['cluster_'+cluster] | map('extract', hostvars) }}" + - name: Disable enterprise repositories apt_repository: repo: '{{ item }}' @@ -40,4 +44,6 @@ - include_tasks: mgmt.yml +- include_tasks: firewall.yml + - include_tasks: frr.yml diff --git a/roles/proxmox/templates/cluster.fw.j2 b/roles/proxmox/templates/cluster.fw.j2 new file mode 100644 index 0000000..5f98e79 --- /dev/null +++ b/roles/proxmox/templates/cluster.fw.j2 @@ -0,0 +1,23 @@ +[OPTIONS] + +enable: 1 + +[RULES] + +IN Ping(ACCEPT) -log nolog # don’t be rude +IN SSH(ACCEPT) -i mgmt # for ansible etc. +IN ACCEPT -source {{ nodes | map('device_address') | flatten | selectattr('family.value', '==', 4) | map(attribute='address') | join(',') }} # my cluster +IN ACCEPT -source {{ nodes | map('device_address') | flatten | selectattr('family.value', '==', 6) | map(attribute='address') | join(',') }} # my cluster +{% 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(range_delimiter=':') %} +{% if prefixes4 %} +IN ACCEPT -source {{ prefixes4 | join(',') }} -p {{ service.protocol }} -dport {{ ports }} # {{ service.name }} +{% endif %} +{% if prefixes6 %} +IN ACCEPT -source {{ prefixes6 | join(',') }} -p {{ service.protocol }} -dport {{ ports }} # {{ service.name }} +{% endif %} +{% endfor %} +