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 os
|
||||
import re
|
||||
import requests
|
||||
import urllib3
|
||||
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"
|
||||
TRAEFIK_INSTANCE = os.getenv('TRAEFIK_INSTANCE') or ''
|
||||
TRAEFIK_HOST = os.getenv('TRAEFIK_HOST') or ''
|
||||
EXTERNAL_HOST = os.getenv('EXTERNAL_HOST') or ''
|
||||
|
||||
INTERNAL_DESTS = (os.getenv('INTERNAL_DESTS') or '').split(',') or []
|
||||
EXTERNAL_DESTS = (os.getenv('EXTERNAL_DESTS') or '').split(',') 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'
|
||||
PORT = os.getenv('PORT') or '80'
|
||||
|
||||
assert NODE_NAME != '_traefik'
|
||||
assert TRAEFIK_INSTANCE
|
||||
assert TRAEFIK_HOST
|
||||
assert INTERNAL_DESTS
|
||||
assert EXTERNAL_DESTS
|
||||
assert PRIVATE_KEY
|
||||
for x in [
|
||||
TRAEFIK_INSTANCE,
|
||||
TRAEFIK_HOST,
|
||||
EXTERNAL_HOST,
|
||||
|
||||
INTERNAL_DESTS,
|
||||
EXTERNAL_DESTS,
|
||||
|
||||
PRIVATE_KEY,
|
||||
CLOUDFLARE_API_KEY,
|
||||
CLOUDFLARE_ZONE_ID,
|
||||
|
||||
HOST,
|
||||
PORT,
|
||||
]: assert x
|
||||
|
||||
app = Flask(__name__)
|
||||
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)})
|
||||
|
||||
|
||||
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:
|
||||
internal: set[str] = set()
|
||||
external: set[str] = set()
|
||||
|
@ -111,10 +134,34 @@ class Observer:
|
|||
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
|
||||
def update(cls, data):
|
||||
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.internal, cls.external = cls.parse_raw_data(data)
|
||||
print(f"{cls.internal=}, {cls.external=}")
|
||||
|
@ -126,7 +173,10 @@ class Observer:
|
|||
@classmethod
|
||||
def parse_raw_data(cls, data: dict):
|
||||
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)
|
||||
external = flt(EXTERNAL_ENTRYPOINT)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue