feat: update cloudflare dns records
This commit is contained in:
parent
5830244af0
commit
5c906ac814
1 changed files with 58 additions and 8 deletions
66
app.py
66
app.py
|
@ -1,7 +1,6 @@
|
||||||
import base64
|
import base64
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import requests
|
|
||||||
import urllib3
|
import urllib3
|
||||||
from concurrent.futures import Future, ThreadPoolExecutor, wait as gather_futures
|
from concurrent.futures import Future, ThreadPoolExecutor, wait as gather_futures
|
||||||
|
|
||||||
|
@ -12,18 +11,34 @@ from flask import Flask, request
|
||||||
NODE_NAME = (os.getenv('NODE_NAME') or '') + "_traefik"
|
NODE_NAME = (os.getenv('NODE_NAME') or '') + "_traefik"
|
||||||
TRAEFIK_INSTANCE = os.getenv('TRAEFIK_INSTANCE') or ''
|
TRAEFIK_INSTANCE = os.getenv('TRAEFIK_INSTANCE') or ''
|
||||||
TRAEFIK_HOST = os.getenv('TRAEFIK_HOST') or ''
|
TRAEFIK_HOST = os.getenv('TRAEFIK_HOST') or ''
|
||||||
|
EXTERNAL_HOST = os.getenv('EXTERNAL_HOST') or ''
|
||||||
|
|
||||||
INTERNAL_DESTS = (os.getenv('INTERNAL_DESTS') or '').split(',') or []
|
INTERNAL_DESTS = (os.getenv('INTERNAL_DESTS') or '').split(',') or []
|
||||||
EXTERNAL_DESTS = (os.getenv('EXTERNAL_DESTS') or '').split(',') or []
|
EXTERNAL_DESTS = (os.getenv('EXTERNAL_DESTS') or '').split(',') or []
|
||||||
|
|
||||||
PRIVATE_KEY = os.getenv('PRIVATE_KEY') or ''
|
PRIVATE_KEY = os.getenv('PRIVATE_KEY') or ''
|
||||||
|
CLOUDFLARE_API_KEY = os.getenv('CLOUDFLARE_API_KEY') or ''
|
||||||
|
CLOUDFLARE_ZONE_ID = os.getenv('CLOUDFLARE_ZONE_ID') or ''
|
||||||
|
|
||||||
HOST = os.getenv('HOST') or '0.0.0.0'
|
HOST = os.getenv('HOST') or '0.0.0.0'
|
||||||
PORT = os.getenv('PORT') or '80'
|
PORT = os.getenv('PORT') or '80'
|
||||||
|
|
||||||
assert NODE_NAME != '_traefik'
|
assert NODE_NAME != '_traefik'
|
||||||
assert TRAEFIK_INSTANCE
|
for x in [
|
||||||
assert TRAEFIK_HOST
|
TRAEFIK_INSTANCE,
|
||||||
assert INTERNAL_DESTS
|
TRAEFIK_HOST,
|
||||||
assert EXTERNAL_DESTS
|
EXTERNAL_HOST,
|
||||||
assert PRIVATE_KEY
|
|
||||||
|
INTERNAL_DESTS,
|
||||||
|
EXTERNAL_DESTS,
|
||||||
|
|
||||||
|
PRIVATE_KEY,
|
||||||
|
CLOUDFLARE_API_KEY,
|
||||||
|
CLOUDFLARE_ZONE_ID,
|
||||||
|
|
||||||
|
HOST,
|
||||||
|
PORT,
|
||||||
|
]: assert x
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
PATTERN = re.compile(r"Host\(`((?:[a-zA-Z0-9_-]+\.)*wzray\.com)`\)")
|
PATTERN = re.compile(r"Host\(`((?:[a-zA-Z0-9_-]+\.)*wzray\.com)`\)")
|
||||||
|
@ -42,6 +57,14 @@ def Connection(host: str, user: str = 'root', port = None) -> fabric.Connection:
|
||||||
{'auth_strategy': paramiko.auth_strategy.InMemoryPrivateKey(user, privkey)})
|
{'auth_strategy': paramiko.auth_strategy.InMemoryPrivateKey(user, privkey)})
|
||||||
|
|
||||||
|
|
||||||
|
def cf_request(method: str, target: str, json = None):
|
||||||
|
r = urllib3.request(method, 'https://api.cloudflare.com/client/v4/' + target,
|
||||||
|
headers={'Authorization': f'Bearer {CLOUDFLARE_API_KEY}'}, json=json)
|
||||||
|
if r.status != 200:
|
||||||
|
raise Exception(f'Error {r.status}: {r.data.decode()}')
|
||||||
|
return r.json()['result']
|
||||||
|
|
||||||
|
|
||||||
class Observer:
|
class Observer:
|
||||||
internal: set[str] = set()
|
internal: set[str] = set()
|
||||||
external: set[str] = set()
|
external: set[str] = set()
|
||||||
|
@ -111,10 +134,34 @@ class Observer:
|
||||||
return [cls.executor.submit(_update, x) for x in EXTERNAL_DESTS]
|
return [cls.executor.submit(_update, x) for x in EXTERNAL_DESTS]
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update_cloudflare(cls):
|
||||||
|
url = f'zones/{CLOUDFLARE_ZONE_ID}/dns_records'
|
||||||
|
to_delete = [{'id': x['id']} for x in filter(
|
||||||
|
lambda x: x['comment'] == NODE_NAME, cf_request('GET', url))]
|
||||||
|
if not cls.external and not to_delete:
|
||||||
|
return
|
||||||
|
cf_request('POST', url + '/batch', {
|
||||||
|
'deletes': to_delete,
|
||||||
|
'posts': [{
|
||||||
|
'comment': NODE_NAME,
|
||||||
|
'content': EXTERNAL_HOST,
|
||||||
|
'type': 'A',
|
||||||
|
'proxied': True,
|
||||||
|
'name': x,
|
||||||
|
|
||||||
|
} for x in cls.external]
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update(cls, data):
|
def update(cls, data):
|
||||||
def _job():
|
def _job():
|
||||||
gather_futures(cls.update_external() + cls.update_internal())
|
gather_futures([
|
||||||
|
*cls.update_external(),
|
||||||
|
*cls.update_internal(),
|
||||||
|
cls.executor.submit(cls.update_cloudflare)
|
||||||
|
])
|
||||||
cls.job = None
|
cls.job = None
|
||||||
cls.internal, cls.external = cls.parse_raw_data(data)
|
cls.internal, cls.external = cls.parse_raw_data(data)
|
||||||
print(f"{cls.internal=}, {cls.external=}")
|
print(f"{cls.internal=}, {cls.external=}")
|
||||||
|
@ -126,7 +173,10 @@ class Observer:
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse_raw_data(cls, data: dict):
|
def parse_raw_data(cls, data: dict):
|
||||||
http = data.get('http') or {}
|
http = data.get('http') or {}
|
||||||
flt = lambda ep: set([x.group(1) for x in [re.match(PATTERN, x['rule']) for x in http['routers'].values() if ep in x['entryPoints']] if x])
|
|
||||||
|
flt = lambda ep: set([z
|
||||||
|
for y in [re.findall(PATTERN, x['rule'])
|
||||||
|
for x in http['routers'].values() if ep in x['entryPoints']] if y for z in y])
|
||||||
|
|
||||||
internal = flt(INTERNAL_ENTRYPOINT)
|
internal = flt(INTERNAL_ENTRYPOINT)
|
||||||
external = flt(EXTERNAL_ENTRYPOINT)
|
external = flt(EXTERNAL_ENTRYPOINT)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue