oshipka/provision/auto_dns/check.py
2020-06-21 17:45:33 +02:00

174 lines
4.4 KiB
Python

import csv
import ipaddress
import threading
from collections import defaultdict
from datetime import datetime
from time import sleep
import sys
import requests
from lexicon.client import Client
from lexicon.config import ConfigResolver
from oshipka.util.process import process_exp_backoff
from sensitive import CLOUDFLARE_USERNAME, CLOUDFLARE_AUTH_KEY
SLEEP_SECONDS = 60 * 30 # 30 min
DEFAULT_TTL = 5 * 60
ipv4_sites = [
"http://www.icanhazip.com",
"http://ipecho.net/plain",
"http://checkip.amazonaws.com",
"http://ipinfo.io/ip",
"http://ifconfig.me"
]
ipv6_sites = [
"https://diagnostic.opendns.com/myip",
"https://ifconfig.co/ip",
]
ip_checkers = ipv4_sites + ipv6_sites
dns_to_check = []
results = []
real = defaultdict(dict)
def test_ip(ip):
try:
ipaddress_info = ipaddress.ip_address(ip)
except ValueError as e:
print(e)
return None
if ipaddress_info.version == 4:
version = 'A'
elif ipaddress_info.version == 6:
version = 'AAAA'
else:
return None
return {
"addr": ipaddress_info.exploded,
"version": version,
}
def fetch_ip(url):
try:
resp = requests.get(url)
ip = resp.text.strip()
ip_info = test_ip(ip)
if ip_info:
results.append(ip_info)
except Exception as e:
print("Error fetching {}: {}".format(url, e))
tso, tsd = [], []
def check_observed():
for url in ip_checkers:
t = threading.Thread(target=fetch_ip, args=(url,))
tso.append(t)
t.start()
def check_dns():
for domain, ipv in dns_to_check:
t = threading.Thread(target=get_dns, args=(domain, ipv,))
tsd.append(t)
t.start()
def get_resolver_results(domain, ipv):
dns_config = ConfigResolver()
dns_config.with_dict({
'provider_name': 'cloudflare',
'action': 'list',
'type': ipv,
'domain': domain,
'cloudflare': {
'auth_username': CLOUDFLARE_USERNAME,
'auth_token': CLOUDFLARE_AUTH_KEY,
}
})
client = Client(dns_config)
return client.execute()
def set_resolver_results(domain, ipv, new):
dns_config = ConfigResolver()
dns_config.with_dict({
'provider_name': 'cloudflare',
'action': 'create',
'type': ipv,
'domain': domain,
'name': domain,
'content': new,
'ttl': DEFAULT_TTL,
'cloudflare': {
'auth_username': CLOUDFLARE_USERNAME,
'auth_token': CLOUDFLARE_AUTH_KEY,
}
})
client = Client(dns_config)
return client.execute()
def get_dns(domain, ipv):
results = process_exp_backoff(get_resolver_results, func_args=[domain, ipv])
res = [x.get('content') for x in results if x.get('name') == domain]
if len(res) == 1:
print("Real {}: {}".format(ipv, res[0]))
real[domain][ipv] = res[0]
def set_dns(domain, ipv, new):
return process_exp_backoff(set_resolver_results, func_args=[domain, ipv, new])
def get_uniq(_list):
if len(_list) > 1 and len(set(_list)) == 1:
return _list[0]
def main():
with open('dns_addresses.csv') as csvf:
csvreader = csv.reader(csvf)
for row in csvreader:
dns_to_check.append(row)
check_dns()
check_observed()
for t in tso:
t.join()
for t in tsd:
t.join()
for domain, version in dns_to_check:
ip_obs = get_uniq([x['addr'] for x in results if x['version'] == version])
print("{}/{}: {}".format(domain, version, ip_obs))
ip_real = real.get(domain).get(version)
sys.stdout.flush()
if ip_obs == ip_real:
print('{}/{} is same!'.format(domain, version))
else:
print("{}/{} diff on dns: real: {}, obs: {}".format(domain, version, ip_real, ip_obs))
if set_dns(domain, version, ip_real):
print("update successful {}/{} -> {}".format(domain, version, ip_real))
else:
print("update failed: {}/{} on dns is {}, but obs is {}".format(
domain, version, ip_real, ip_obs
))
if __name__ == "__main__":
while True:
print("{}: Waking up".format(datetime.utcnow()))
sys.stdout.flush()
main()
print("{}: Sleeping for {}".format(datetime.utcnow(), SLEEP_SECONDS))
sys.stdout.flush()
sleep(SLEEP_SECONDS)