From 3402b91b04553d7fc5d22f061ddd879970fb5535 Mon Sep 17 00:00:00 2001 From: Ian Fijolek Date: Tue, 21 Jan 2020 17:44:02 -0800 Subject: [PATCH] Contain entire build in Dockerfile Rather than pulling and storing the py file, having start script and fetching of qemu files here, everything can just be done in the Dockerfile. --- .drone.yml | 36 ------------ Dockerfile | 20 ++++--- Makefile | 29 ++++------ README.md | 27 +++++---- get_qemu.sh | 13 ----- requirements.txt | 1 - start.sh | 3 - update_ddns.py | 139 ----------------------------------------------- 8 files changed, 40 insertions(+), 228 deletions(-) delete mode 100755 get_qemu.sh delete mode 100644 requirements.txt delete mode 100755 start.sh delete mode 100755 update_ddns.py diff --git a/.drone.yml b/.drone.yml index 0745922..8298811 100644 --- a/.drone.yml +++ b/.drone.yml @@ -2,18 +2,6 @@ kind: pipeline name: linux-amd64 steps: - - name: get qemu - image: ubuntu:bionic - commands: - - apt-get update - - apt-get install -y make wget - - make build/qemu-x86_64-static - when: - branch: - - master - event: - - push - - tag - name: build image: plugins/docker @@ -37,18 +25,6 @@ kind: pipeline name: linux-arm steps: - - name: get qemu - image: ubuntu:bionic - commands: - - apt-get update - - apt-get install -y make wget - - make build/qemu-arm-static - when: - branch: - - master - event: - - push - - tag - name: build image: plugins/docker @@ -75,18 +51,6 @@ kind: pipeline name: linux-arm64 steps: - - name: get qemu - image: ubuntu:bionic - commands: - - apt-get update - - apt-get install -y make wget - - make build/qemu-aarch64-static - when: - branch: - - master - event: - - push - - tag - name: build image: plugins/docker diff --git a/Dockerfile b/Dockerfile index 5b94bcb..2dcb384 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,21 +1,27 @@ ARG REPO=library + +FROM multiarch/qemu-user-static:4.1.0-1 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-alpine +# Copy mutliarch file to run builds on x86_64 ARG ARCH=x86_64 -COPY ./build/qemu-${ARCH}-static /usr/bin/ - +COPY --from=qemu-user-static /usr/bin/qemu-${ARCH}-* /usr/bin/ RUN mkdir -p /src WORKDIR /src -COPY ./requirements.txt ./ -RUN pip install -r ./requirements.txt +# Get Cloudflare example script +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 +x ./update_ddns.py -COPY ./start.sh ./ -COPY ./update_ddns.py ./update_ddns.py +RUN pip install cloudflare==$CF_VERSION ENV DOMAIN="" USER nobody -CMD [ "/src/start.sh" ] +ENTRYPOINT [ "/src/update_ddns.py" ] diff --git a/Makefile b/Makefile index ec80dfe..fcf90f4 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ DOCKER_TAG ?= cloudflare-ddns-dev-${USER} +.PHONY: clean .PHONY: default default: test @@ -7,35 +8,25 @@ default: test test: @echo ok -.PHONY: update -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 - .PHONY: build -build: build/qemu-x86_64-static +build: docker build . -t ${DOCKER_TAG} -build/qemu-arm-static: - ./get_qemu.sh arm - -build/qemu-x86_64-static: - ./get_qemu.sh x86_64 - -build/qemu-aarch64-static: - ./get_qemu.sh aarch64 - .PHONY: cross-build-arm -cross-build-arm: build/qemu-arm-static +cross-build-arm: docker build --build-arg REPO=arm32v6 --build-arg ARCH=arm . -t ${DOCKER_TAG}-linux-arm .PHONY: cross-build-arm64 -cross-build-arm64: build/qemu-aarch64-static +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 - docker run --rm -e DOMAIN=${DOMAIN} \ + docker run --rm \ -e CF_API_EMAIL=${CF_API_EMAIL} \ -e CF_API_KEY=${CF_API_KEY} \ - ${DOCKER_TAG} + ${DOCKER_TAG} \ + "${DOMAIN}" diff --git a/README.md b/README.md index 04591a1..e1c1735 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,28 @@ # 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 -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 - CF_API_EMAIL=admin@example.com + ddns: + 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_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. 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 diff --git a/get_qemu.sh b/get_qemu.sh deleted file mode 100755 index 9ea3b81..0000000 --- a/get_qemu.sh +++ /dev/null @@ -1,13 +0,0 @@ -#! /bin/bash - -HOST_ARCH=x86_64 -VERSION=v2.9.1-1 - -mkdir -p build -cd build - -for target_arch in $*; do - wget -N https://github.com/multiarch/qemu-user-static/releases/download/$VERSION/${HOST_ARCH}_qemu-${target_arch}-static.tar.gz - tar -xvf ${HOST_ARCH}_qemu-${target_arch}-static.tar.gz - rm ${HOST_ARCH}_qemu-${target_arch}-static.tar.gz -done diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 0a9630a..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -cloudflare diff --git a/start.sh b/start.sh deleted file mode 100755 index 2993157..0000000 --- a/start.sh +++ /dev/null @@ -1,3 +0,0 @@ -#! /bin/sh - -exec /src/update_ddns.py $DOMAIN diff --git a/update_ddns.py b/update_ddns.py deleted file mode 100755 index 8f0239c..0000000 --- a/update_ddns.py +++ /dev/null @@ -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() -