From 2f970cd71c38c06355408e76c0c0961d4016b4ef Mon Sep 17 00:00:00 2001 From: Gilles Grandou Date: Sun, 4 Oct 2020 17:41:05 +0200 Subject: [PATCH] add livebox firewall support --- dyndomain | 90 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 11 deletions(-) diff --git a/dyndomain b/dyndomain index ec05c50..7c9f189 100755 --- a/dyndomain +++ b/dyndomain @@ -116,18 +116,18 @@ def full_name(host, domain): return '.'.join([host, domain]) -def zone_add_entry(zone, prot, name, addr, stamp): - #print('zone_add_entry: {} {} {} {}'.format(prot, name, addr, stamp)) +def zone_add_entry(zone, prot, key, value, stamp): + #print('zone_add_entry: {} {} {} {}'.format(prot, key, value, stamp)) if not zone.get(prot): zone[prot] = {} - if not zone[prot].get(name): - zone[prot][name] = {} - if not zone[prot][name].get(addr): - zone[prot][name][addr] = { 'first': stamp } - zone[prot][name][addr]['last'] = stamp + if not zone[prot].get(key): + zone[prot][key] = {} + if not zone[prot][key].get(value): + zone[prot][key][value] = { 'first': stamp } + zone[prot][key][value]['last'] = stamp -def populate_zone(zone, wan_hostname, wan_addr, hosts, hosts_list, hosts_nat, domain, stamp): +def populate_zone(zone, wan_hostname, wan_addr, hosts, hosts_list, hosts_nat, pinhole_list, domain, stamp): wan_hostname = full_name(wan_hostname, domain) if not zone.get('A'): @@ -140,12 +140,19 @@ def populate_zone(zone, wan_hostname, wan_addr, hosts, hosts_list, hosts_nat, do for host in hosts_nat: if hosts['A'].get(host): zone_add_entry(zone, 'A', full_name(host, domain), wan_addr['ipv4'], stamp) + # we can only add PortNat entry for on Address, + # so let's arbitrarely take the 1st one + for port in hosts_nat[host]: + zone_add_entry(zone, 'nat', hosts['A'][host][0], port, stamp) if wan_addr['ipv6'] != '': zone_add_entry(zone, 'AAAA', wan_hostname, wan_addr['ipv6'], stamp) for host in hosts_list: for addr in hosts['AAAA'].get(host, []): zone_add_entry(zone, 'AAAA', full_name(host, domain), addr, stamp) + for port in pinhole_list.get(host, []): + zone_add_entry(zone, 'pin', addr, port, stamp) + def process_zone(zone, stamp, grace_period, sync_zone): @@ -213,8 +220,8 @@ def ovh_update_zone(domain, zone, update, sync_zone): client = ovh.Client() if sync_zone: - for prot in zone: - for name in zone[prot]: + for prot in ['A', 'AAAA']: + for name in zone.get(prot, []): result = client.get('/domain/zone/{}/record'.format(domain), fieldType=prot, subDomain=name) @@ -223,6 +230,8 @@ def ovh_update_zone(domain, zone, update, sync_zone): client.delete('/domain/zone/{}/record/{}'.format(domain, id)) for prot, name, addr in update['delete']: + if not prot in ['A', 'AAAA']: + continue result = client.get('/domain/zone/%s/record' % domain, fieldType=prot, subDomain=name) @@ -233,6 +242,8 @@ def ovh_update_zone(domain, zone, update, sync_zone): client.delete('/domain/zone/%s/record/%d' % (domain, id)) for prot, name, addr in update['add']: + if not prot in ['A', 'AAAA']: + continue print("OVH: create entry for %s %s %s" % (name, prot, addr)) client.post('/domain/zone/%s/record' % domain, fieldType=prot, @@ -247,6 +258,62 @@ def ovh_update_zone(domain, zone, update, sync_zone): print('OVH update error\n') return False +def livebox_rule_id(words): + id='dyndomain_{}'.format('_'.join(words)) + id = id.replace('.', '_') + return id + +def livebox_delete_port_nat(port, addr): + print('livebox: delete PortNat {} to {}'.format(port, addr)) + id = livebox_rule_id([port]) + r = sysbus.requete('Firewall:deletePortForwarding', { 'id': id, 'origin': 'webui' }) + +def livebox_delete_pinhole(port, addr): + print('livebox: delete pinhole {} to {}'.format(port, addr)) + id = livebox_rule_id([port, addr]) + r = sysbus.requete('Firewall:deletePinhole', { 'id': id, 'origin': 'webui' }) + +def livebox_add_port_nat(port, addr): + print('livebox: add PortNat {} to {}'.format(port, addr)) + id = livebox_rule_id([port]) + a = { + 'id': id, + 'origin': 'webui', + 'sourceInterface': 'data', + 'destinationIPAddress': addr, + 'protocol': '6', + 'internalPort': port, + 'enable': True, + } + r = sysbus.requete('Firewall:setPortForwarding', a) + +def livebox_add_pinhole(port, addr): + print('livebox: add pinhole {} to {}'.format(port, addr)) + id = livebox_rule_id([port, addr]) + a = { + 'id': id, + 'origin': 'webui', + 'sourceInterface': 'data', + 'destinationPort': port, + 'destinationIPAddress': addr, + 'protocol': '6', + 'enable': True, + } + r = sysbus.requete('Firewall:setPinhole', a) + +def livebox_update_fw(zone, update, sync_zone): + for prot, addr, port in update['delete']: + if prot == 'nat': + livebox_delete_port_nat(port, addr) + elif prot == 'pin': + livebox_delete_pinhole(port, addr) + for prot, addr, port in update['add']: + if prot == 'nat': + livebox_add_port_nat(port, addr) + elif prot == 'pin': + livebox_add_pinhole(port, addr) + + def send_update_mail(mail_to, mail_from, zone_domain, update, mail_ignore_list, wan): okmail=False @@ -311,12 +378,13 @@ if not zone: sync_zone = True stamp = int(time.time()) -populate_zone(zone, wan_hostname, wan, hosts, hosts_list, nat_list, zone_subdomain, stamp) +populate_zone(zone, wan_hostname, wan, hosts, hosts_list, nat_list, pinhole_list, zone_subdomain, stamp) update = process_zone(zone, stamp, zone_timeout, sync_zone) if update: log_update_zone(update) success = ovh_update_zone(zone_domain, zone, update, sync_zone) + livebox_update_fw(zone, update, sync_zone) if success: send_update_mail(mail_to, mail_from, zone_domain, update, mail_ignore_list, wan)