Compare commits
18 Commits
multiarch-
...
master
Author | SHA1 | Date |
---|---|---|
IamTheFij | b5e99939fb | |
IamTheFij | 01dc2dd149 | |
IamTheFij | 4702310823 | |
IamTheFij | aef73188f2 | |
IamTheFij | 9a8445e6ec | |
IamTheFij | a752c67dad | |
IamTheFij | 2c9b26a283 | |
IamTheFij | ff2d50b6e8 | |
IamTheFij | d66c11cc00 | |
IamTheFij | fdb3c744b0 | |
IamTheFij | d9b20c4759 | |
IamTheFij | 2b41676824 | |
IamTheFij | 176feef895 | |
IamTheFij | 3402b91b04 | |
IamTheFij | c1125b9310 | |
IamTheFij | 1cb69ad575 | |
IamTheFij | 8a39181d22 | |
IamTheFij | 52dced87a8 |
|
@ -0,0 +1,109 @@
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: linux-amd64
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- tag
|
||||||
|
refs:
|
||||||
|
- refs/heads/master
|
||||||
|
- refs/tags/v*
|
||||||
|
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
image: plugins/docker
|
||||||
|
settings:
|
||||||
|
repo: iamthefij/cloudflare-ddns
|
||||||
|
auto_tag: true
|
||||||
|
auto_tag_suffix: linux-amd64
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: linux-arm
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- tag
|
||||||
|
refs:
|
||||||
|
- refs/heads/master
|
||||||
|
- refs/tags/v*
|
||||||
|
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
image: plugins/docker
|
||||||
|
settings:
|
||||||
|
repo: iamthefij/cloudflare-ddns
|
||||||
|
auto_tag: true
|
||||||
|
auto_tag_suffix: linux-arm
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
build_args:
|
||||||
|
- ARCH=arm
|
||||||
|
- REPO=arm32v7
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: linux-arm64
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- tag
|
||||||
|
refs:
|
||||||
|
- refs/heads/master
|
||||||
|
- refs/tags/v*
|
||||||
|
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: build
|
||||||
|
image: plugins/docker
|
||||||
|
settings:
|
||||||
|
repo: iamthefij/cloudflare-ddns
|
||||||
|
auto_tag: true
|
||||||
|
auto_tag_suffix: linux-arm64
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
||||||
|
build_args:
|
||||||
|
- ARCH=aarch64
|
||||||
|
- REPO=arm64v8
|
||||||
|
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: manifest
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- linux-amd64
|
||||||
|
- linux-arm
|
||||||
|
- linux-arm64
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- push
|
||||||
|
- tag
|
||||||
|
refs:
|
||||||
|
- refs/heads/master
|
||||||
|
- refs/tags/v*
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: publish manifest
|
||||||
|
image: plugins/manifest
|
||||||
|
settings:
|
||||||
|
spec: manifest.tmpl
|
||||||
|
auto_tag: true
|
||||||
|
ignore_missing: true
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
password:
|
||||||
|
from_secret: docker_password
|
|
@ -0,0 +1,3 @@
|
||||||
|
qemu-*
|
||||||
|
*.tar.gz
|
||||||
|
tags
|
26
Dockerfile
26
Dockerfile
|
@ -1,14 +1,28 @@
|
||||||
FROM python:3-alpine
|
ARG REPO=library
|
||||||
|
|
||||||
|
FROM multiarch/qemu-user-static:4.2.0-2 as qemu-user-static
|
||||||
|
# Make sure a dummy x86_64 file exists so that the copy command doesn't error
|
||||||
|
# RUN touch /usr/bin/qemu-x86_64-fake
|
||||||
|
|
||||||
|
FROM ${REPO}/python:3.8-alpine
|
||||||
|
|
||||||
|
# Copy mutliarch file to run builds on x86_64
|
||||||
|
ARG ARCH=x86_64
|
||||||
|
COPY --from=qemu-user-static /usr/bin/qemu-* /usr/bin/
|
||||||
|
# COPY --from=qemu-user-static /usr/bin/qemu-${ARCH}-* /usr/bin/
|
||||||
|
|
||||||
RUN mkdir -p /src
|
RUN mkdir -p /src
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
COPY ./requirements.txt ./
|
# Get Cloudflare example script
|
||||||
RUN pip install -r ./requirements.txt
|
ENV CF_VERSION=2.6.0
|
||||||
|
ADD https://raw.githubusercontent.com/cloudflare/python-cloudflare/$CF_VERSION/examples/example_update_dynamic_dns.py ./update_ddns.py
|
||||||
|
RUN chmod +rx ./update_ddns.py
|
||||||
|
|
||||||
COPY ./start.sh ./
|
RUN python -m pip install --no-cache-dir cloudflare==$CF_VERSION
|
||||||
COPY ./update_ddns.py ./update_ddns.py
|
|
||||||
|
|
||||||
ENV DOMAIN=""
|
ENV DOMAIN=""
|
||||||
|
|
||||||
CMD [ "/src/start.sh" ]
|
USER nobody
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/src/update_ddns.py" ]
|
||||||
|
|
26
Makefile
26
Makefile
|
@ -1,18 +1,32 @@
|
||||||
DOCKER_TAG ?= cloudflare-ddns-dev-${USER}
|
DOCKER_TAG ?= cloudflare-ddns-dev-${USER}
|
||||||
|
.PHONY: clean
|
||||||
|
|
||||||
|
.PHONY: default
|
||||||
default: test
|
default: test
|
||||||
|
|
||||||
|
.PHONY:test
|
||||||
test:
|
test:
|
||||||
@echo ok
|
@echo ok
|
||||||
|
|
||||||
update:
|
.PHONY: build
|
||||||
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:
|
build:
|
||||||
docker build . -t ${DOCKER_TAG}
|
docker build . -t ${DOCKER_TAG}
|
||||||
|
|
||||||
|
.PHONY: cross-build-arm
|
||||||
|
cross-build-arm:
|
||||||
|
docker build --build-arg REPO=arm32v7 --build-arg ARCH=arm . -t ${DOCKER_TAG}-linux-arm
|
||||||
|
|
||||||
|
.PHONY: cross-build-arm64
|
||||||
|
cross-build-arm64:
|
||||||
|
docker build --build-arg REPO=arm64v8 --build-arg ARCH=aarch64 . -t ${DOCKER_TAG}-linux-arm64
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: build cross-build-arm cross-build-arm64
|
||||||
|
|
||||||
|
.PHONY: run
|
||||||
run: build
|
run: build
|
||||||
docker run --rm -e DOMAIN=${DOMAIN} \
|
docker run --rm \
|
||||||
-e CF_API_EMAIL=${CF_API_EMAIL} \
|
-e CF_API_EMAIL=${CF_API_EMAIL} \
|
||||||
-e CF_API_KEY=${CF_API_KEY} \
|
-e CF_API_KEY=${CF_API_KEY} \
|
||||||
${DOCKER_TAG}
|
${DOCKER_TAG} \
|
||||||
|
"${DOMAIN}"
|
||||||
|
|
27
README.md
27
README.md
|
@ -1,21 +1,34 @@
|
||||||
# Docker Cloudfare DDNS
|
# Docker Cloudfare DDNS
|
||||||
|
|
||||||
Simple Docker image to dynamically update a Cloudflare DNS record.
|
Simple Docker image that wraps an example script to dynamically update a Cloudflare DNS record.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
All parameters are passed to the script using env variables, so export the following:
|
There are two things to configure. First, the domain that you wish to update needs to be provided as a command line argument. This can be done by adding it to the end of your run command (example in the Makefile) or by adding it as a command to your compose file. Eg:
|
||||||
|
|
||||||
DOMAIN=sub.example.com
|
ddns:
|
||||||
CF_API_EMAIL=admin@example.com
|
image: IamTheFij/cloudflare-ddns
|
||||||
|
command: ["example.com"]
|
||||||
|
|
||||||
|
Your Cloudflare credentials can be passed in any way that [python-cloudflare](https://github.com/cloudflare/python-cloudflare) allows. Generally, either via envioronment variables:
|
||||||
|
|
||||||
|
CF_API_EMAIL=admin@example.com # Do not set if using an API Token
|
||||||
CF_API_KEY=00000000000000000000
|
CF_API_KEY=00000000000000000000
|
||||||
|
CF_API_CERTKEY='v1.0-...'
|
||||||
|
|
||||||
|
Or by providing a file mounted to the working directory in the image, `/src/.cloudflare.cfg` that contains something like:
|
||||||
|
|
||||||
|
[CloudFlare]
|
||||||
|
emal = admin@example.com # Do not set if using an API Token
|
||||||
|
token = 00000000000000000000
|
||||||
|
certtoken = v1.0-...
|
||||||
|
|
||||||
Then run. To execute from this directory, you can use the convenient Make target.
|
Then run. To execute from this directory, you can use the convenient Make target.
|
||||||
|
|
||||||
make run
|
make run
|
||||||
|
|
||||||
## Development
|
## Source
|
||||||
|
|
||||||
The script is straight from the examples provided by Cloudflare on their Github. The latest version can be downloaded using:
|
Original source: https://git.iamthefij.com/iamthefij/docker-cloudflare-ddns
|
||||||
|
|
||||||
make update
|
Github mirror: https://github.com/iamthefij/docker-cloudflare-ddns
|
|
@ -0,0 +1,25 @@
|
||||||
|
image: iamthefij/cloudflare-ddns:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
|
||||||
|
{{#if build.tags}}
|
||||||
|
tags:
|
||||||
|
{{#each build.tags}}
|
||||||
|
- {{this}}
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
manifests:
|
||||||
|
-
|
||||||
|
image: iamthefij/cloudflare-ddns:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
|
||||||
|
platform:
|
||||||
|
architecture: amd64
|
||||||
|
os: linux
|
||||||
|
-
|
||||||
|
image: iamthefij/cloudflare-ddns:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
|
||||||
|
platform:
|
||||||
|
architecture: arm64
|
||||||
|
os: linux
|
||||||
|
variant: v8
|
||||||
|
-
|
||||||
|
image: iamthefij/cloudflare-ddns:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
|
||||||
|
platform:
|
||||||
|
architecture: arm
|
||||||
|
os: linux
|
||||||
|
variant: v7
|
|
@ -1 +0,0 @@
|
||||||
cloudflare
|
|
139
update_ddns.py
139
update_ddns.py
|
@ -1,139 +0,0 @@
|
||||||
#!/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