8 minutes
Integration of a local Cloudflare domain update service for use with Fritz!Box DynDNS feature
This is a follow-up of the How-to-use-Fritz!Box-DynDNS-feature-with-cloudflare guide. As you may have noticed, the solution described in the old guide no longer works with newer versions of Fritz!OS. This is because the Fritz!Box doesn’t allow http DynDNS updates anymore, even if they point to local IPs. This guide will provide an updated solution and clean up the old solution using docker compose.
Understanding certificates and challenges
Digital certificates and CAs
A digital certificate is a file hosted on a server to enable encryption. It contains the public key and information about it, information about the identity of its owner (called the subject), and the digital signature of an entity that has verified the contents of the certificate (called the issuer). This allows others (relying parties) to rely upon signatures or on assertions made about the private key that corresponds to the certified public key. The issuer, typically a Certificate Authority (CA), acts as a trusted third party, trusted by both the subject (owner) of the certificate and a relying party (client). A client uses the CA certificate to authenticate the CA signature on the server certificate as part of the authorization before initiating a secure connection. Typically, client software, such as browsers, includes a set of trusted CA certificates.
Certificates issued and signed by a trusted CA can then be used to establish secure connections to a server. There are essential in order to circumvent a malicious party which happens to be on the route to a target server which acts as if it were the target, i.e. man-in-the-middle attack.
In other, simpler words
A digital certificate is like a passport for a server, ensuring secure and trustworthy communication over the internet. It contains:
- A public key for encryption,
- Information about the owner,
- Verification from a trusted entity (like a Certificate Authority).
This helps others trust the website’s identity and encryption. A Certificate Authority (CA) acts as a trusted middleman, verifying the certificate’s details. When you connect to a server, the CA’s signature on the certificate gets checked to make sure it’s legitimate. Without these certificates, attackers are able to intercept communication.
ACME Challenges
The Automatic Certificate Management Environment (ACME) protocol is a communications protocol for automating interactions between certificate authorities and their users’ servers, enabling the automated deployment of public key infrastructure. Let’s Encrypt is a certificate authority that uses this protocol to verify that you control the domain names in the certificates you want to get signed using “challenges”. There are two common challenges:
- HTTP: With the HTTP challenge, the ACME client proves ownership of a domain by placing a specific file at a specific URL on the web server associated with that domain. The ACME server then makes an HTTP request to retrieve this file from the specified URL. If the file is found and contains the expected content, the challenge is considered successful.
- DNS: With the DNS challenge, the ACME client proves ownership of a domain by adding a specific DNS record to the domain’s DNS zone. The ACME server then performs a DNS lookup to verify the presence of this DNS record. If the record is found and contains the expected value, the challenge is considered successful.
Prerequisites
Before proceeding, make sure you have the following:
- A working server on your home network with docker i.e. docker-compose installed on it,
- A Cloudflare account with a domain added to it,
- A Fritz!Box router with the DynDNS feature.
Step 1: Create a Cloudflare API Token
Exactly as described in the previous guide
- Login to your cloudflare account https://dash.cloudflare.com/login.
- Then visit https://dash.cloudflare.com/profile/api-tokens
- Click on ‘Create Token’.
- Set the permissions to allow “Zone - Zone - Read” and “Zone - DNS - Edit”.
- (optional) You can restrict the access to a specific domain via the Zone Resources. With “Include - Specific
zone -
<your domain>
” the API key will only work to modify the DNS records of your specified domain. - Then click on the ‘Continue to summary’ button, then click on ‘Create token’ and copy the token for future use.
Step 2: Write the Python Webserver
Exactly as described in the previous guide
The following python file app.py
is a minimal example of an IPv4 DNS record update.
An explanation of this file can be found in the previous guide.
import CloudFlare
import flask
app = flask.Flask(__name__)
@app.route('/', methods=['GET'])
def main():
token = flask.request.args.get('token')
zone = flask.request.args.get('zone')
ipv4 = flask.request.args.get('ipv4')
cf = CloudFlare.CloudFlare(token=token)
zones = cf.zones.get(params={'name': zone})
a_record = cf.zones.dns_records.get(zones[0]['id'], params={
'name': '{}'.format(zone), 'match': 'all', 'type': 'A'})
cf.zones.dns_records.put(zones[0]['id'], a_record[0]['id'], data={
'name': a_record[0]['name'], 'type': 'A', 'content': ipv4, 'proxied': a_record[0]['proxied'],
'ttl': a_record[0]['ttl']})
return flask.jsonify({'status': 'success', 'message': 'Update successful.'}), 200
import os
import waitress
app.secret_key = os.urandom(24)
waitress.serve(app, host='0.0.0.0', port=80)
Step 3: Setting up Caddy reverse proxy with DNS challenge
Caddy is a powerful and easy-to-use web server that emphasizes simplicity and automatic configuration. We want to use its reverse proxy capabilities to route incoming requests to different backend servers. In our case, these different backend servers are different Docker containers. Caddy has a built-in ACME client to automatically obtain certificates for your website from Let’s Encrypt.
Up until now, i.e. since the last guide, the Python web server has been running within the local network. Ideally, this should remain the case. However, this means that also the webserver cannot be accessed from the internet. But this is essential for the HTTP challenge to obtain a trusted certificate for encryption. However, the DNS challenge is appropriate, because it proves ownership of the domain at the DNS provider. Unfortunately, the default caddy Docker container does not provide support for the Let’s encrypt DNS challenge. Thus, we need to customize the caddy installation:
- First, create a folder with the name
caddy
and cd into it.mkdir caddy && cd caddy
- Within this folder, create another folder for build purposes and also cd into it.
Name it
caddy-build
.mkdir caddy-build && cd caddy-build
- Here, create a
Dockerfile
with the following content:nano Dockerfile
ARG VERSION=2 FROM caddy:${VERSION}-builder AS builder RUN xcaddy build \ --with github.com/caddy-dns/cloudflare FROM caddy:${VERSION} COPY --from=builder /usr/bin/caddy /usr/bin/caddy
- Go back to the folder below and create the file
container-vars.env
.Fill the file with the following:cd .. && nano container-vars.env
Of course replaceMY_DOMAIN=<domain> CLOUDFLARE_API_TOKEN=<API token of domain>
<domain>
and<API token of domain>
with your values. - Then create the file
docker-compose.yml
with the following content:nano docker-compose.yml
version: "3.7" services: caddy: build: ./caddy-build hostname: caddy container_name: caddy restart: unless-stopped cap_add: - NET_ADMIN ports: - 80:80 - 443:443 - 443:443/udp volumes: - ./Caddyfile:/etc/caddy/Caddyfile - ./site:/srv - ./data:/data - ./config:/config - /etc/localtime:/etc/localtime:ro env_file: - container-vars.env networks: - caddynet networks: caddynet: external: true
- The above docker-compose file specifies an external network.
Because we want to organize i.e. encapsulate the various docker containers which are sitting behind the reverse proxy we will use a docker network.
To create it, do the following:
docker network create caddynet
- Now you can start the container running
docker-compose up -d
Step 4: Configure docker compose file for flask server.
- First, create a folder with the name
dyndns
and cd into it.mkdir dyndns && cd dyndns
- Within this folder, create another folder for build purposes and also cd into it.
Name it
dyndns-build
.mkdir dyndns-build && cd dyndns-build
- Copy or move the Python file containing the webserver code into the lastly created folder.
cp <path to Python file> .
- Then create a
Dockerfile
with the following content:nano Dockerfile
FROM python:3-alpine WORKDIR /app RUN pip install --no-cache-dir cloudflare Flask waitress COPY app.py ./ CMD [ "python", "./app.py" ] EXPOSE 80
- Go back to the folder below and create the file
docker-compose.yml
.Fill the file with the following:cd .. && nano docker-compose.yml
version: '3.7' services: dyndns: container_name: dyndns build: ./dyndns-build hostname: dyndns restart: unless-stopped networks: - caddynet networks: caddynet: external: true
- Now you can start the container running
docker-compose up -d
Step 5: Associate subdomain with local IP
With the web server and caddy reverse proxy set up, all we need to do is tie everything together to be able to connect to a local IP with https enabled.
- Log into Fritz!Box and navigate to http://fritz.box/#homeNet.
- Find your server by its hostname and click Edit.
- Then move to
IP Adresses
and check the boxAlways assign the same IPv4 address to this network device
. Also copy the local IP of the server for later use. - Go to https://dash.cloudflare.com/ and navigate to
Websites
. Then click on your domain and after that onDNS
. - Add a record with type
A
. Usedyndns
for the name and the local IP from above for the IPv4 address. - Go to the caddy folder and edit the Caddyfile, adding the following:
dyndns.{$MY_DOMAIN} { reverse_proxy dyndns:80 tls { dns cloudflare {env.CLOUDFLARE_API_TOKEN} } }
- Reload caddy with
docker exec caddy caddy reload --adapter caddyfile --config /etc/caddy/Caddyfile
- Go again to the Fritz!Box web ui and navigate to http://fritz.box/#dyndns.
- There put in the following values
- Update-URL:
Replace
https://dyndns.<your doamin>/?token=<pass>&zone=<domain>&ipv4=<ipaddr>
<your domain>
with the domain you bought from Cloudflare. - Domainname: The domain you bought from Cloudflare.
- Benutzername: You can leave it blank, it is not used.
- Kennwort: The Cloudflare API Token you created earlier.
- Update-URL:
- Lastly navigate to http://fritz.box/#netSet.
Scroll down and click on
more settings
. There add<your domain>
anddyndns.<your doamin>
to the DNS rebind protection field.
Now that all is set, Fritz!Box can update the IP via the CloudFlare API.
1552 Words
2024-03-22 00:02