ADD import roles
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
import ipaddress
|
||||
|
||||
class FilterModule(object):
|
||||
def filters(self):
|
||||
return {
|
||||
'generate_radvd': self.generate_radvd,
|
||||
'generate_keepalived': self.generate_keepalived,
|
||||
'generate_interfaces': self.generate_interfaces,
|
||||
'generate_nftables': self.generate_nftables,
|
||||
'generate_dhcpd': self.generate_dhcpd,
|
||||
'generate_ospf': self.generate_ospf,
|
||||
}
|
||||
|
||||
def generate_radvd(self, cluster_networks):
|
||||
result = []
|
||||
for net in cluster_networks:
|
||||
if 'prefix' not in net:
|
||||
continue
|
||||
if 'dns' in net:
|
||||
dns = net['dns']
|
||||
else:
|
||||
dns = ['2a00:1398::64:1', '2a00:1398::64:2']
|
||||
radvd = {
|
||||
'name': net['interface'],
|
||||
'prefix': net['prefix'],
|
||||
'source': 'fe80::1',
|
||||
'dns': dns,
|
||||
}
|
||||
result.append(radvd)
|
||||
return result
|
||||
|
||||
def generate_keepalived(self, cluster_networks):
|
||||
result = []
|
||||
for net in cluster_networks:
|
||||
keepalived = {
|
||||
'name': net['interface'],
|
||||
'addresses': ['fe80::1/64'],
|
||||
}
|
||||
result.append(keepalived)
|
||||
if 'prefix_v4' in net:
|
||||
keepalived = {
|
||||
'name': net['interface'] + '_v4',
|
||||
'interface': net['interface'],
|
||||
'addresses': [self.nth_addr_v4(net['prefix_v4'], 1)],
|
||||
}
|
||||
result.append(keepalived)
|
||||
return result
|
||||
|
||||
def nth_addr(self, prefix: str, n:int) -> str:
|
||||
network = ipaddress.IPv6Network(prefix)
|
||||
# Check if n is within the valid range
|
||||
if n >= network.num_addresses or n < 0:
|
||||
raise ValueError(f"n must be between 0 and {network.num_addresses - 1}")
|
||||
# Calculate the n-th address
|
||||
nth_address = network.network_address + n
|
||||
return str(nth_address) + '/' + str(network.prefixlen)
|
||||
|
||||
def nth_addr_v4(self, prefix: str, n:int, with_prefix: bool = True) -> str:
|
||||
network = ipaddress.IPv4Network(prefix)
|
||||
# Check if n is within the valid range
|
||||
if n >= network.num_addresses or n < 0:
|
||||
raise ValueError(f"n must be between 0 and {network.num_addresses - 1}")
|
||||
# Calculate the n-th address
|
||||
nth_address = network.network_address + n
|
||||
if with_prefix:
|
||||
return str(nth_address) + '/' + str(network.prefixlen)
|
||||
else:
|
||||
return str(nth_address)
|
||||
|
||||
def generate_interfaces(self, cluster_networks, addr_num):
|
||||
result = []
|
||||
for net in cluster_networks:
|
||||
interface = {
|
||||
'name': net['interface'],
|
||||
'mac': net['mac'],
|
||||
}
|
||||
if 'prefix' in net:
|
||||
inet6 = {
|
||||
'addr': [self.nth_addr(net['prefix'], addr_num)],
|
||||
'gateway': False,
|
||||
'custom': False,
|
||||
'routes': False,
|
||||
}
|
||||
interface['inet6'] = inet6
|
||||
if 'prefix_v4' in net:
|
||||
inet4 = {
|
||||
'addr': [self.nth_addr_v4(net['prefix_v4'], addr_num)],
|
||||
'gateway': False,
|
||||
'custom': False,
|
||||
'routes': False,
|
||||
}
|
||||
interface['inet'] = inet4
|
||||
result.append(interface)
|
||||
return result
|
||||
|
||||
def clean_port_range(self, port_range):
|
||||
if ('{' not in port_range) and ('-' in port_range or ',' in port_range):
|
||||
return '{{' + port_range + '}}'
|
||||
return port_range
|
||||
|
||||
def generate_nftables(self, cluster_networks, upstream_interface):
|
||||
result = []
|
||||
nat_rules = []
|
||||
default_policies = []
|
||||
for net in cluster_networks:
|
||||
if 'prefix' in net:
|
||||
if net.get('firewall', None) is not None and net['firewall'].get('enabled', False) == True:
|
||||
for rule in net['firewall'].get('rules', []):
|
||||
if rule.get('dst', None) is None:
|
||||
rule['dst'] = '%net'
|
||||
|
||||
nft_rule = 'oifname {} '.format(net['interface'])
|
||||
if rule.get('src', '') != '':
|
||||
nft_rule += 'ip6 saddr {} '.format(rule['src'].replace('%net', net['prefix']))
|
||||
if rule.get('dst', '') != '':
|
||||
nft_rule += 'ip6 daddr {} '.format(rule['dst'].replace('%net', net['prefix']))
|
||||
if rule.get('sport', '') != '':
|
||||
nft_rule += '{{proto}} sport {} '.format(self.clean_port_range(rule['sport']))
|
||||
if rule.get('dport', '') != '':
|
||||
nft_rule += '{{proto}} dport {} '.format(self.clean_port_range(rule['dport']))
|
||||
nft_rule += rule['action']
|
||||
for proto in rule.get('protocols', ['tcp', 'udp']):
|
||||
result.append(nft_rule.format(proto=proto))
|
||||
default_policies.append('oifname {} ip6 daddr {} {}'.format(net['interface'], net['prefix'], net['firewall']['policy']))
|
||||
else:
|
||||
default_policies.append('oifname {} ip6 daddr {} {}'.format(net['interface'], net['prefix'], 'accept'))
|
||||
# NAT
|
||||
if net.get('nat', None) is not None and net['nat'].get('enabled', False) == True:
|
||||
if net['nat'].get('source', None) is not None and net.get('prefix_v4', None) is not None:
|
||||
nat_rules.append('ip saddr {prefix} oif {dest_iface} snat to {source}'.format(prefix=net['prefix_v4'], dest_iface=upstream_interface, source=net['nat']['source']))
|
||||
|
||||
default_policies.append('oifname {} accept'.format(upstream_interface))
|
||||
|
||||
full_rules = "\n".join(result + default_policies)
|
||||
|
||||
full_nat = "\n".join(nat_rules)
|
||||
|
||||
ret = [
|
||||
{
|
||||
'name': 'cluster_router_bundle',
|
||||
'rule': full_rules,
|
||||
'chain': 'forward',
|
||||
},
|
||||
{
|
||||
'name': 'cluster_router_bundle',
|
||||
'rule': full_nat,
|
||||
'chain': 'postrouting',
|
||||
}
|
||||
]
|
||||
return ret
|
||||
|
||||
def is_v4(self, addr: str) -> bool:
|
||||
try:
|
||||
ipaddress.IPv4Address(addr)
|
||||
return True
|
||||
except ipaddress.AddressValueError:
|
||||
return False
|
||||
|
||||
def generate_dhcpd(self, cluster_networks):
|
||||
result = []
|
||||
for net in cluster_networks:
|
||||
if net.get('prefix_v4', None) is None:
|
||||
continue
|
||||
if net.get('dhcp', None) is None or net['dhcp'].get('enabled', False) == False:
|
||||
continue
|
||||
router = self.nth_addr_v4(net['prefix_v4'], 1, False)
|
||||
subnet = self.nth_addr_v4(net['prefix_v4'], 0, False)
|
||||
netmask = str(ipaddress.IPv4Network(net['prefix_v4']).netmask)
|
||||
range_start = self.nth_addr_v4(net['prefix_v4'], net['dhcp'].get('range_start', 20), False)
|
||||
range_end = self.nth_addr_v4(net['prefix_v4'], net['dhcp'].get('range_end', 150), False)
|
||||
dhcpd = {
|
||||
'interface': net['interface'],
|
||||
'subnet': subnet,
|
||||
'router': router,
|
||||
'netmask': netmask,
|
||||
'range_start': range_start,
|
||||
'range_end': range_end,
|
||||
}
|
||||
dns = ', '.join(list(filter(lambda x: self.is_v4(x), net.get('dns', []))))
|
||||
if len(dns) > 0:
|
||||
dhcpd['dns'] = dns
|
||||
result.append(dhcpd)
|
||||
return result
|
||||
|
||||
def generate_ospf(self, cluster_networks):
|
||||
result = []
|
||||
for net in cluster_networks:
|
||||
ospf = {
|
||||
'name': net['interface'],
|
||||
'stub': True,
|
||||
}
|
||||
result.append(ospf)
|
||||
return result
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
- name: Generate radvd config
|
||||
set_fact:
|
||||
radvd_interfaces: "{{ cluster_networks | generate_radvd | union(radvd_interfaces | default([])) }}"
|
||||
- name: Generate keepalived config
|
||||
set_fact:
|
||||
keepalived_interfaces: "{{ cluster_networks | generate_keepalived | union(keepalived_interfaces | default([])) }}"
|
||||
- name: Generate interfaces config
|
||||
set_fact:
|
||||
net_interfaces: "{{ cluster_networks | generate_interfaces(cluster_v6_end) | union(net_interfaces | default([])) }}"
|
||||
- name: Generate firewall config
|
||||
set_fact:
|
||||
nftables_rules: "{{ cluster_networks | generate_nftables(cluster_upstream_interface) | union(nftables_rules | default([])) }}"
|
||||
- name: Generate dhcp config
|
||||
set_fact:
|
||||
isc_dhcp_networks: "{{ cluster_networks | generate_dhcpd | union(isc_dhcp_networks | default([])) }}"
|
||||
# - name: Generate ospf config
|
||||
# set_fact:
|
||||
# bird_ospf_interfaces: "{{ cluster_networks | generate_ospf | union(bird_ospf_interfaces | default([])) }}"
|
||||
Reference in New Issue
Block a user