From 4d78196461cceb9b247a4c6bcc50a1efd1d1e627 Mon Sep 17 00:00:00 2001 From: Michael Mandl Date: Sun, 24 May 2020 19:18:39 +0200 Subject: [PATCH] Find own ipv6 address and loop updates --- Dockerfile | 6 +++- docker-compose.yml | 4 +++ src/ddns_update.py | 74 ++++++++++++++++++++++++++++++-------------- src/requirements.txt | 2 ++ 4 files changed, 61 insertions(+), 25 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9b8f8f5..2e7c0d3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,5 +3,9 @@ RUN pip install --no-cache-dir --upgrade pip WORKDIR /app COPY src/ ./ -RUN pip install --no-cache-dir --requirement requirements.txt + +ARG APK_BUILD_DEPS=".build-deps gcc musl-dev linux-headers" +RUN apk add --no-cache --virtual $APK_BUILD_DEPS\ + && pip install --no-cache-dir --requirement requirements.txt \ + && apk del $APK_BUILD_DEPS CMD [ "python", "ddns_update.py" ] diff --git a/docker-compose.yml b/docker-compose.yml index 32860bf..d44706b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,4 +5,8 @@ services: container_name: strato-ddns build: context: . + network_mode: host restart: always + environment: + - DDNS_HOST=my.host.name + - DDNS_KEY=secret diff --git a/src/ddns_update.py b/src/ddns_update.py index f5497e0..a62bd05 100755 --- a/src/ddns_update.py +++ b/src/ddns_update.py @@ -5,15 +5,54 @@ import requests from requests.exceptions import RequestException import click from os import environ +from netifaces import interfaces, ifaddresses, AF_INET6 +from ipaddress import ip_address +from time import sleep logging.basicConfig(level=logging.DEBUG) log = logging.getLogger("ddns updater") +def get_global_address(address_family): + for interface in interfaces(): + if_addresses = ifaddresses(interface) + if address_family in if_addresses: + for address in if_addresses[address_family]: + addr = ip_address(address["addr"].split("%")[0]) + if addr.is_global: + return addr + return None + + +def get_global_ipv6(): + return get_global_address(AF_INET6) + + def response_successful(response_text): return response_text.startswith("good") or response_text.startswith("nochg") +def ddns_update(host, key, ip): + url = f"https://dyndns.strato.com/nic/update?hostname={host}&myip={ip}" + + try: + response = requests.get(url, auth=(host, key), timeout=3) + response.raise_for_status() + + if not response_successful(response.text): + raise RequestException("update failed") + + except RequestException as error: + log.debug(response.text.strip()) + log.error(error) + return False + + log.debug(response.text.strip()) + log.info("update successful") + + return True + + @click.command() @click.option( "--host", @@ -27,30 +66,17 @@ def response_successful(response_text): default=lambda: environ.get("DDNS_KEY", None), help="The ddns authorization key", ) -@click.option( - "--ip", - required=True, - default=lambda: environ.get("DDNS_IP", None), - help="The hosts new ip address in ipv4 or ipv6 format", -) -def ddns_update(host, key, ip): - url = f"https://dyndns.strato.com/nic/update?hostname={host}&myip={ip}" - - try: - response = requests.get(url, auth=(host, key), timeout=3) - response.raise_for_status() - - if not response_successful(response.text): - raise RequestException("update failed") - - except RequestException as error: - log.debug(response.text.strip()) - log.error(error) - return - - log.debug(response.text.strip()) - log.info("update successful") +def loop_ddns_update(host, key): + last_ip = None + while True: + current_ip = get_global_ipv6() + if current_ip != last_ip: + if ddns_update(host, key, current_ip): + last_ip = current_ip + else: + log.debug("skipped update, ip address unchanged") + sleep(5) if __name__ == "__main__": - ddns_update() + loop_ddns_update() diff --git a/src/requirements.txt b/src/requirements.txt index 8574798..24ecc48 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,2 +1,4 @@ Click==7.0 requests==2.23.0 +netifaces==0.10.9 +ipaddress==1.0.23