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.
This commit is contained in:
parent
c1125b9310
commit
3402b91b04
36
.drone.yml
36
.drone.yml
@ -2,18 +2,6 @@ kind: pipeline
|
|||||||
name: linux-amd64
|
name: linux-amd64
|
||||||
|
|
||||||
steps:
|
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
|
- name: build
|
||||||
image: plugins/docker
|
image: plugins/docker
|
||||||
@ -37,18 +25,6 @@ kind: pipeline
|
|||||||
name: linux-arm
|
name: linux-arm
|
||||||
|
|
||||||
steps:
|
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
|
- name: build
|
||||||
image: plugins/docker
|
image: plugins/docker
|
||||||
@ -75,18 +51,6 @@ kind: pipeline
|
|||||||
name: linux-arm64
|
name: linux-arm64
|
||||||
|
|
||||||
steps:
|
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
|
- name: build
|
||||||
image: plugins/docker
|
image: plugins/docker
|
||||||
|
20
Dockerfile
20
Dockerfile
@ -1,21 +1,27 @@
|
|||||||
ARG REPO=library
|
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
|
FROM ${REPO}/python:3-alpine
|
||||||
|
|
||||||
|
# Copy mutliarch file to run builds on x86_64
|
||||||
ARG ARCH=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
|
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 +x ./update_ddns.py
|
||||||
|
|
||||||
COPY ./start.sh ./
|
RUN pip install cloudflare==$CF_VERSION
|
||||||
COPY ./update_ddns.py ./update_ddns.py
|
|
||||||
|
|
||||||
ENV DOMAIN=""
|
ENV DOMAIN=""
|
||||||
|
|
||||||
USER nobody
|
USER nobody
|
||||||
|
|
||||||
CMD [ "/src/start.sh" ]
|
ENTRYPOINT [ "/src/update_ddns.py" ]
|
||||||
|
29
Makefile
29
Makefile
@ -1,4 +1,5 @@
|
|||||||
DOCKER_TAG ?= cloudflare-ddns-dev-${USER}
|
DOCKER_TAG ?= cloudflare-ddns-dev-${USER}
|
||||||
|
.PHONY: clean
|
||||||
|
|
||||||
.PHONY: default
|
.PHONY: default
|
||||||
default: test
|
default: test
|
||||||
@ -7,35 +8,25 @@ default: test
|
|||||||
test:
|
test:
|
||||||
@echo ok
|
@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
|
.PHONY: build
|
||||||
build: build/qemu-x86_64-static
|
build:
|
||||||
docker build . -t ${DOCKER_TAG}
|
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
|
.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
|
docker build --build-arg REPO=arm32v6 --build-arg ARCH=arm . -t ${DOCKER_TAG}-linux-arm
|
||||||
|
|
||||||
.PHONY: cross-build-arm64
|
.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
|
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
|
.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,28 @@
|
|||||||
# 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
|
|
||||||
|
|
||||||
The script is straight from the examples provided by Cloudflare on their Github. The latest version can be downloaded using:
|
|
||||||
|
|
||||||
make update
|
|
||||||
|
13
get_qemu.sh
13
get_qemu.sh
@ -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
|
|
@ -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
Block a user