Working commit
This commit is contained in:
parent
05f4df2ebb
commit
57d53b936c
2
.dockerignore
Normal file
2
.dockerignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
README.md
|
||||||
|
Makefile
|
14
Dockerfile
Normal file
14
Dockerfile
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
FROM python:3
|
||||||
|
|
||||||
|
RUN mkdir -p /src
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
COPY ./requirements.txt ./
|
||||||
|
RUN pip install -r ./requirements.txt
|
||||||
|
|
||||||
|
COPY ./start.sh ./
|
||||||
|
COPY ./update_ddns.py ./update_ddns.py
|
||||||
|
|
||||||
|
ENV DOMAIN=""
|
||||||
|
|
||||||
|
CMD [ "/src/start.sh" ]
|
18
Makefile
Normal file
18
Makefile
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
DOCKER_TAG ?= cloudflare-ddns-dev-${USER}
|
||||||
|
default: test
|
||||||
|
|
||||||
|
test:
|
||||||
|
@echo ok
|
||||||
|
|
||||||
|
update:
|
||||||
|
curl -o update_ddns.py https://raw.githubusercontent.com/cloudflare/python-cloudflare/master/examples/example_update_dynamic_dns.py
|
||||||
|
chmod +x update_ddns.py
|
||||||
|
|
||||||
|
build:
|
||||||
|
docker build . -t ${DOCKER_TAG}
|
||||||
|
|
||||||
|
run: build
|
||||||
|
docker run --rm -e DOMAIN=${DOMAIN} \
|
||||||
|
-e CF_API_EMAIL=${CF_API_EMAIL} \
|
||||||
|
-e CF_API_KEY=${CF_API_KEY} \
|
||||||
|
${DOCKER_TAG}
|
22
README.md
22
README.md
@ -1,3 +1,21 @@
|
|||||||
# docker-cloudflare-ddns
|
# Docker Cloudfare DDNS
|
||||||
|
|
||||||
Docker build of a Cloudflare DDNS client using their example code
|
Simple Docker image to dynamically update a Cloudflare DNS record.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
All parameters are passed to the script using env variables, so export the following:
|
||||||
|
|
||||||
|
DOMAIN=sub.example.com
|
||||||
|
CF_API_EMAIL=admin@example.com
|
||||||
|
CF_API_KEY=00000000000000000000
|
||||||
|
|
||||||
|
Then run. To execute from this directory, you can use the convenient Make target.
|
||||||
|
|
||||||
|
make run
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
The script is straight from the examples provided by Cloudflare on their Github. The latest version can be downloaded using:
|
||||||
|
|
||||||
|
make update
|
||||||
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
cloudflare
|
139
update_ddns.py
Executable file
139
update_ddns.py
Executable file
@ -0,0 +1,139 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""Cloudflare API code - example"""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
import requests
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.abspath('..'))
|
||||||
|
import CloudFlare
|
||||||
|
|
||||||
|
def my_ip_address():
|
||||||
|
"""Cloudflare API code - example"""
|
||||||
|
|
||||||
|
# This list is adjustable - plus some v6 enabled services are needed
|
||||||
|
# url = 'http://myip.dnsomatic.com'
|
||||||
|
# url = 'http://www.trackip.net/ip'
|
||||||
|
# url = 'http://myexternalip.com/raw'
|
||||||
|
url = 'https://api.ipify.org'
|
||||||
|
try:
|
||||||
|
ip_address = requests.get(url).text
|
||||||
|
except:
|
||||||
|
exit('%s: failed' % (url))
|
||||||
|
if ip_address == '':
|
||||||
|
exit('%s: failed' % (url))
|
||||||
|
|
||||||
|
if ':' in ip_address:
|
||||||
|
ip_address_type = 'AAAA'
|
||||||
|
else:
|
||||||
|
ip_address_type = 'A'
|
||||||
|
|
||||||
|
return ip_address, ip_address_type
|
||||||
|
|
||||||
|
def do_dns_update(cf, zone_name, zone_id, dns_name, ip_address, ip_address_type):
|
||||||
|
"""Cloudflare API code - example"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
params = {'name':dns_name, 'match':'all', 'type':ip_address_type}
|
||||||
|
dns_records = cf.zones.dns_records.get(zone_id, params=params)
|
||||||
|
except CloudFlare.exceptions.CloudFlareAPIError as e:
|
||||||
|
exit('/zones/dns_records %s - %d %s - api call failed' % (dns_name, e, e))
|
||||||
|
|
||||||
|
updated = False
|
||||||
|
|
||||||
|
# update the record - unless it's already correct
|
||||||
|
for dns_record in dns_records:
|
||||||
|
old_ip_address = dns_record['content']
|
||||||
|
old_ip_address_type = dns_record['type']
|
||||||
|
|
||||||
|
if ip_address_type not in ['A', 'AAAA']:
|
||||||
|
# we only deal with A / AAAA records
|
||||||
|
continue
|
||||||
|
|
||||||
|
if ip_address_type != old_ip_address_type:
|
||||||
|
# only update the correct address type (A or AAAA)
|
||||||
|
# we don't see this becuase of the search params above
|
||||||
|
print('IGNORED: %s %s ; wrong address family' % (dns_name, old_ip_address))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if ip_address == old_ip_address:
|
||||||
|
print('UNCHANGED: %s %s' % (dns_name, ip_address))
|
||||||
|
updated = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Yes, we need to update this record - we know it's the same address type
|
||||||
|
|
||||||
|
dns_record_id = dns_record['id']
|
||||||
|
dns_record = {
|
||||||
|
'name':dns_name,
|
||||||
|
'type':ip_address_type,
|
||||||
|
'content':ip_address
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
dns_record = cf.zones.dns_records.put(zone_id, dns_record_id, data=dns_record)
|
||||||
|
except CloudFlare.exceptions.CloudFlareAPIError as e:
|
||||||
|
exit('/zones.dns_records.put %s - %d %s - api call failed' % (dns_name, e, e))
|
||||||
|
print('UPDATED: %s %s -> %s' % (dns_name, old_ip_address, ip_address))
|
||||||
|
updated = True
|
||||||
|
|
||||||
|
if updated:
|
||||||
|
return
|
||||||
|
|
||||||
|
# no exsiting dns record to update - so create dns record
|
||||||
|
dns_record = {
|
||||||
|
'name':dns_name,
|
||||||
|
'type':ip_address_type,
|
||||||
|
'content':ip_address
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
dns_record = cf.zones.dns_records.post(zone_id, data=dns_record)
|
||||||
|
except CloudFlare.exceptions.CloudFlareAPIError as e:
|
||||||
|
exit('/zones.dns_records.post %s - %d %s - api call failed' % (dns_name, e, e))
|
||||||
|
print('CREATED: %s %s' % (dns_name, ip_address))
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Cloudflare API code - example"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
dns_name = sys.argv[1]
|
||||||
|
except IndexError:
|
||||||
|
exit('usage: example-update-dynamic-dns.py fqdn-hostname')
|
||||||
|
|
||||||
|
host_name, zone_name = dns_name.split('.', 1)
|
||||||
|
|
||||||
|
ip_address, ip_address_type = my_ip_address()
|
||||||
|
|
||||||
|
print('MY IP: %s %s' % (dns_name, ip_address))
|
||||||
|
|
||||||
|
cf = CloudFlare.CloudFlare()
|
||||||
|
|
||||||
|
# grab the zone identifier
|
||||||
|
try:
|
||||||
|
params = {'name':zone_name}
|
||||||
|
zones = cf.zones.get(params=params)
|
||||||
|
except CloudFlare.exceptions.CloudFlareAPIError as e:
|
||||||
|
exit('/zones %d %s - api call failed' % (e, e))
|
||||||
|
except Exception as e:
|
||||||
|
exit('/zones.get - %s - api call failed' % (e))
|
||||||
|
|
||||||
|
if len(zones) == 0:
|
||||||
|
exit('/zones.get - %s - zone not found' % (zone_name))
|
||||||
|
|
||||||
|
if len(zones) != 1:
|
||||||
|
exit('/zones.get - %s - api call returned %d items' % (zone_name, len(zones)))
|
||||||
|
|
||||||
|
zone = zones[0]
|
||||||
|
|
||||||
|
zone_name = zone['name']
|
||||||
|
zone_id = zone['id']
|
||||||
|
|
||||||
|
do_dns_update(cf, zone_name, zone_id, dns_name, ip_address, ip_address_type)
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user