first commit

This commit is contained in:
2025-11-27 17:31:29 +01:00
commit 4b81ed91f1
5 changed files with 142 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
compose.yaml
.venv
.env

13
Dockerfile Normal file
View File

@@ -0,0 +1,13 @@
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
COPY cloudflare-dns-docker.py .
RUN pip install -r requirements.txt
ENV PYTHONUNBUFFERED=1
VOLUME ["/var/run/docker.sock"]
CMD ["python", "./cloudflare-dns-docker.py"]

104
cloudflare-dns-docker.py Normal file
View File

@@ -0,0 +1,104 @@
#!/usr/bin/env python3
import os
import time
import requests
import docker
import tldextract
from cloudflare import Cloudflare
CLOUDFLARE_TOKEN = os.getenv("CLOUDFLARE_TOKEN")
PUBLIC_IPV4 = os.getenv("PUBLIC_IPV4")
PUBLIC_IPV6 = os.getenv("PUBLIC_IPV6") # optional
if not CLOUDFLARE_TOKEN or not PUBLIC_IPV4:
raise ValueError("CLOUDFLARE_TOKEN and PUBLIC_IPV4 must be set")
cf = Cloudflare(
api_token = CLOUDFLARE_TOKEN,
)
def get_zone_id(zone):
zones = cf.zones.list(
name = zone
).result
if zones == []:
return None
return zones[0].id
def get_record(fqdn):
records = cf.dns.records.list(
zone_id = zone_id,
name = fqdn
).result
return records
def create_record(zone_id, fqdn, ip, record_type, proxied):
record_response = cf.dns.records.create(
zone_id = zone_id,
name = fqdn,
ttl = 3600,
type = record_type,
content = ip,
proxied = proxied,
)
print("Created " + record_type + " " + fqdn)
def delete_record(zone_id, fqdn):
records = get_record(fqdn)
for r in records:
cf.dns.records.delete(
dns_record_id = r.id,
zone_id = zone_id,
)
print("Removed " + r.type + " " + r.name)
def handle_event(event):
status = event.get("status")
attrs = event.get("Actor", {}).get("Attributes", {})
if attrs.get("cloudflare.enable") == "true":
# should we proxy the domain ?
if attrs.get("cloudflare.proxied") == "true":
proxied = True
else:
proxied = False
# check we have a fqdn defined
if attrs.get("cloudflare.fqdn") == None:
print(attrs.get("name") + ": No FQDN is given, skipping")
return
# break down domain name and get zone id
fqdn = attrs.get("cloudflare.fqdn")
fqdn_extracted = tldextract.extract(fqdn)
subdomain = fqdn_extracted.subdomain
domain = fqdn_extracted.top_domain_under_public_suffix
# get zone id
zone_id = get_zone_id(domain)
if zone_id == None:
print("Error: zone " + domain + " not found")
return
# run actions
if status == "start":
create_record(zone_id, fqdn, PUBLIC_IPV4, "A", proxied)
if PUBLIC_IPV6:
create_record(zone_id, fqdn, PUBLIC_IPV6, "AAAA", proxied)
elif status == "stop":
delete_record(zone_id, fqdn)
def main():
print(f"Starting cloudflare-dns-docker agent with IPv4: {PUBLIC_IPV4}, IPv6: {PUBLIC_IPV6}")
client = docker.from_env()
while True:
try:
filters = {"type": "container"}
for event in client.events(decode=True, filters=filters):
handle_event(event)
except Exception as e:
print(f"Error: {e}")
time.sleep(5)
if __name__ == "__main__":
main()

5
requirements.txt Normal file
View File

@@ -0,0 +1,5 @@
python-dotenv
docker
requests
cloudflare
tldextract

17
shell.nix Normal file
View File

@@ -0,0 +1,17 @@
with import <nixpkgs> {};
mkShell {
buildInputs = [
python311
python311Packages.pip
];
shellHook = ''
echo "Setting up Python 3.11 virtualenv..."
python3.11 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install cloudflare docker requests python-dotenv
'';
}