Files
homelab/k8s-ng/apps/mtproxy/link-reconciler.py
T

75 lines
2.7 KiB
Python
Raw Normal View History

"""Publish client links for every external node, using its public DNS name."""
import base64
import json
import urllib.error
import urllib.parse
import urllib.request
API = "https://kubernetes.default.svc"
NAMESPACE = open("/var/run/secrets/kubernetes.io/serviceaccount/namespace", encoding="utf-8").read().strip()
TOKEN = open("/var/run/secrets/kubernetes.io/serviceaccount/token", encoding="utf-8").read().strip()
CA = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
def request(method, path, body=None):
import ssl
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(
API + path,
data=data,
method=method,
headers={
"Authorization": "Bearer " + TOKEN,
"Content-Type": "application/json",
},
)
with urllib.request.urlopen(req, context=ssl.create_default_context(cafile=CA), timeout=15) as response:
return json.load(response)
def publish(name, links):
path = f"/api/v1/namespaces/{NAMESPACE}/secrets"
data = {node: base64.b64encode(link.encode()).decode() for node, link in links.items()}
try:
current = request("GET", f"{path}/{name}")
except urllib.error.HTTPError as error:
if error.code != 404:
raise
request("POST", path, {"apiVersion": "v1", "kind": "Secret", "metadata": {"name": name}, "type": "Opaque", "data": data})
return
if current.get("data", {}) == data:
return
# PUT replaces the data map, removing links for nodes that are no longer external.
request("PUT", f"{path}/{name}", {
"apiVersion": "v1",
"kind": "Secret",
"metadata": {"name": name, "resourceVersion": current["metadata"]["resourceVersion"]},
"type": "Opaque",
"data": data,
})
secret = open("/config/SECRET", encoding="utf-8").read().strip()
if len(secret) != 32 or any(char not in "0123456789abcdefABCDEF" for char in secret):
raise ValueError("Vaultwarden Secret must be 32 hexadecimal characters")
selector = urllib.parse.quote("homelab.hexor.cy/exposure=external", safe="")
nodes = request("GET", f"/api/v1/nodes?labelSelector={selector}")["items"]
if not nodes:
raise ValueError("No external nodes found")
classic = {}
tls = {}
for node in nodes:
name = node["metadata"]["name"]
# DNS is maintained outside the cluster; no public IP needs to be stored on a Node.
host = f"{name}.hexor.cy"
classic[name] = f"tg://proxy?server={host}&port=30443&secret={secret}"
tls[name] = f"tg://proxy?server={host}&port=30444&secret=ee{secret}79612e7275"
publish("mtproxy-links", classic)
publish("telemt-links", tls)
print(f"Published MTProxy links for {len(nodes)} external nodes")