From 75440a83b01cd46b9436a6ff90d628082319077f Mon Sep 17 00:00:00 2001 From: Ken Fallon Date: Fri, 7 Aug 2026 13:20:43 +0200 Subject: [PATCH] 2026-08-07_13-20-43_CEST --- ccdn/CDN_Update_Script.md | 396 +++++++++++++++++++++++++ ccdn/ccdn_architecture.md | 47 ++- ccdn/ccdn_architecture_upgrades.md | 268 +++++++++++++++++ ccdn/ccdn_docker_image_requirements.md | 213 +++++++++++++ ccdn/hpr.minisign.1785664421.pubkey | 2 + 5 files changed, 922 insertions(+), 4 deletions(-) create mode 100644 ccdn/CDN_Update_Script.md create mode 100644 ccdn/ccdn_architecture_upgrades.md create mode 100644 ccdn/ccdn_docker_image_requirements.md create mode 100644 ccdn/hpr.minisign.1785664421.pubkey diff --git a/ccdn/CDN_Update_Script.md b/ccdn/CDN_Update_Script.md new file mode 100644 index 0000000..1c70c3a --- /dev/null +++ b/ccdn/CDN_Update_Script.md @@ -0,0 +1,396 @@ +# CDN Update Script + +This document describes a reference implementation of the `cdn-update.sh` automation script for the Community CDN architecture. + +The script is designed to: + +* Download the signed control file +* Verify its signature +* Validate JSON syntax +* Generate fail2ban configuration +* Generate nginx configuration +* Select an available origin +* Synchronize content using rsync +* Export Prometheus metrics +* Fail safely when configuration validation fails + +--- + +# Reference Script + +```bash +#!/usr/bin/env bash + +set -euo pipefail + +############################################################################### +# Configuration +############################################################################### + +BASE="/var/lib/cdn" + +CONFIG_DIR="${BASE}/config" +CONTENT_DIR="${BASE}/content" +METRICS_DIR="${BASE}/metrics" + +CONTROL_URL="https://control.example.org/hpr.ccdn.settings.json" +SIG_URL="https://control.example.org/hpr.ccdn.settings.json.minisig" + +PUBKEY="RWQxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + +CONTROL_FILE="${CONFIG_DIR}/hpr.ccdn.settings.json" +SIG_FILE="${CONFIG_DIR}/hpr.ccdn.settings.json.minisig" + +NGINX_GEN="/etc/nginx/conf.d/cdn-generated.conf" + +FAIL2BAN_JAIL="/etc/fail2ban/jail.d/cdn-generated.local" + +METRICS_FILE="${METRICS_DIR}/cdn.prom" + +TMPDIR="$(mktemp -d)" + +############################################################################### +# Metrics helper +############################################################################### + +metric_write() { + cat > "${METRICS_FILE}" < "${FAIL2BAN_JAIL}" < "${NGINX_GEN}" < + Public key, as a base64 string +# -s + Secret key file (default: ~/.minisign/minisign.key +minisign -G -p hpr.minisign.${minisign_date}.pubkey -s hpr.minisign.${minisign_date}.seckey +``` +### Document Signing + +``` +# -S Sign files +# -m +# File to sign/verify +# -t +# Add a one-line trusted comment +# -x +# Signature file (default: .minisig) +# -P + Public key, as a base64 string + +minisign_date=1785664421 +minisign -Sm hpr.ccdn.settings.json -x hpr.ccdn.settings.json.minisig -p hpr.minisign.${minisign_date}.pubkey -t 'Last updated on $( \date --universal +%Y-%m-%dT%H:%M:%SZ_%A ) ($( \date --universal +%s )) by $USER' + +``` + + +## Client + +``` +minisign_date="$( \date --universal +%s )" +echo "Generating keys with date \"${minisign_date}\"" +minisign -G -p hpr.minisign.${minisign_date}.pubkey -s hpr.minisign.${minisign_date}.seckey +``` + -* minisign -* signify -* GnuPG Nodes must reject unsigned or invalid control files. diff --git a/ccdn/ccdn_architecture_upgrades.md b/ccdn/ccdn_architecture_upgrades.md new file mode 100644 index 0000000..644da65 --- /dev/null +++ b/ccdn/ccdn_architecture_upgrades.md @@ -0,0 +1,268 @@ +# ccdn_architecture_upgrades.md + +# CCDN Architecture Upgrade Ideas + +This document lists potential improvements to the CCDN architecture that were identified during a design review. None of these are considered essential for the initial release; they are intended as future enhancements once the core system is stable. + +The philosophy remains unchanged: + +* Keep the system simple. +* Prefer proven Unix tools. +* Avoid unnecessary dependencies. +* Preserve the stateless nature of edge nodes. + +--- + +## 1. Atomic Content Deployment + +**Priority:** High + +Instead of synchronising directly into the live content directory, use a staging directory. + +Suggested workflow: + +1. Download the signed control file. +2. Verify its signature. +3. Synchronise content into a staging directory. +4. Perform validation. +5. Atomically rename the staging directory into production. +6. Reload nginx if required. + +Benefits: + +* Prevents partially updated content from being served. +* Makes interrupted synchronisations harmless. +* Provides clean rollback behaviour if validation fails. + +--- + +## 2. Content Manifest + +**Priority:** High + +Publish a manifest alongside the content containing: + +* filename +* size +* SHA-256 checksum +* modification time + +After synchronisation, each node can verify downloaded files against the manifest. + +Benefits: + +* Detects storage corruption. +* Detects incomplete synchronisations. +* Simplifies troubleshooting. +* Provides confidence that mirrors contain identical content. + +--- + +## 3. Configuration Versioning + +**Priority:** Medium + +Include a version number or timestamp in the signed control file. + +Example: + +```json +{ + "version": 14 +} +``` + +or + +```json +{ + "generated": "2026-08-01T12:00:00Z" +} +``` + +Benefits: + +* Easier troubleshooting. +* Prevents accidental rollback. +* Makes monitoring simpler. + +--- + +## 4. Configuration Expiry + +**Priority:** Medium + +Include an expiry timestamp in the signed control file. + +Example: + +```json +{ + "valid_until": "2026-09-01T00:00:00Z" +} +``` + +Nodes should continue serving existing content if the configuration expires, but generate warnings or alerts so administrators know updates are no longer being received. + +Benefits: + +* Detects long-term communication failures. +* Helps identify replay attacks using stale configurations. + +--- + +## 5. Health Endpoint + +**Priority:** Medium + +Expose a small JSON document such as: + +``` +/health.json +``` + +Example information: + +* configuration version +* last successful synchronisation +* current origin +* disk usage +* software version +* node identifier + +Benefits: + +* Simplifies monitoring. +* Easy integration with Prometheus or external monitoring. +* Useful during troubleshooting. + +--- + +## 6. Disk Space Protection + +**Priority:** Medium + +Before synchronisation, verify sufficient free disk space exists. + +If available space falls below a configured threshold, abort the update and generate an alert. + +Benefits: + +* Prevents failed deployments. +* Protects nodes from filling the filesystem. + +--- + +## 7. SSH Restrictions + +**Priority:** Medium + +Restrict the rsync account so it cannot obtain an interactive shell. + +Possible approaches include: + +* `rrsync` +* `ForceCommand` +* `command=` restrictions in `authorized_keys` + +Benefits: + +* Reduces the impact of a compromised SSH key. +* Limits access strictly to file synchronisation. + +--- + +## 8. Signing Key Rotation + +**Priority:** Low + +Support publishing both the current and next public signing keys. + +This allows new keys to be distributed before they become active. + +Benefits: + +* Simplifies planned key rotation. +* Avoids emergency replacement procedures. + +--- + +## 9. Split Configuration Files + +**Priority:** Low + +If the control file becomes large, consider splitting it into independently signed files. + +For example: + +* origins.json +* sync.json +* nginx.json +* security.json + +Benefits: + +* Easier maintenance. +* Smaller updates. +* Simpler reviews. + +This is not recommended until the configuration grows significantly. + +--- + +## 10. Declarative Configuration Only + +**Priority:** Ongoing + +The signed configuration should describe desired system state rather than commands to execute. + +For example: + +Prefer: + +* sync interval +* fail2ban settings +* nginx options +* origin list + +Avoid: + +* arbitrary shell commands +* remote script execution + +Benefits: + +* Smaller attack surface. +* Easier auditing. +* More predictable behaviour. + +--- + +# Future Enhancements + +These ideas are intentionally outside the scope of the first implementation but may become useful if CCDN grows. + +* Delta manifests for large libraries. +* Geographic origin selection. +* Optional peer-to-peer mirror synchronisation. +* Compression for text-based metadata. +* Signed content release tags. +* Read-only content mounts between updates. + +--- + +# Deliberately Out of Scope + +The following technologies are intentionally excluded from the CCDN design: + +* Kubernetes +* Docker Swarm +* Redis +* PostgreSQL +* Elasticsearch +* Message queues +* Dynamic service discovery +* Distributed databases +* Complex orchestration systems + +The goal is to keep CCDN easy to understand, easy to operate, and easy to recover by relying on mature Unix tooling rather than additional infrastructure. diff --git a/ccdn/ccdn_docker_image_requirements.md b/ccdn/ccdn_docker_image_requirements.md new file mode 100644 index 0000000..d9a98f8 --- /dev/null +++ b/ccdn/ccdn_docker_image_requirements.md @@ -0,0 +1,213 @@ +# Community Content Delivery Network Docker nodes + +## Background + +We wish to deploy a Community CDN that is easy to deploy and maintain. + +This can be achieved by providing a Community Managed docker nodes that is regularly updated. + +Changes can be applied to the GitTea, and the mirror nodes should pick up the changes. + +## Removal of node + +- Remove from central DNS +- Remove from monitoring +- Remove from allow lists +- Remove from authorized_keys + +## + +- Addition of new nodes +- Removal of old nodes + + + + +Path Validation Strategy + +This is where your design differs from a normal web server. + +You know: + +episode numbers +valid extensions +exact file list + +Therefore every request can be validated. + +Option 1 (recommended) + +Allow nginx to serve files normally. + +If file does not exist: + +error_page 404 = @invalid_request; + +location @invalid_request { + access_log /var/log/nginx/invalid-paths.log; + return 404; +} + +This scales well even with tens of thousands of files. + +Prevent Directory Browsing + +autoindex off; + +location / { + try_files $uri =404; +} + +No listings. + +No traversal. + +No guessing directories. + + + + + + + +thank you - I have refined the requirements now so ignore what I said earlier. + +We are building a private community content delivery network, with nodes that serves files that typically change once a day. + +## Hardware Requirements + +Requirements for Hosting + +- 24/7 Home Service +- fixed IP address +- unlimited bandwidth +- fast > 500mb/sec upload +- large > 5T of storage +- permission from your ISP to run a web server +- Contact information know to the Janitors +- Optional: UPS + +## Software updates + + +The nodes should ideally run on both [Docker](https://en.wikipedia.org/wiki/Docker_%28software%29) and [Podman](https://en.wikipedia.org/wiki/Podman) + +It should run Debian `debian:stable-slim` + +All software MUST be running verifiable Free Libre Open Source software with links to the license. + +The nodes should have as little utilities as possible to do it's job and in order to reduce the attack surface. + +The amount of traffic is about 5Mb/sec with a load of 2 TPS + +There will be many nodes, that will come and go over time. + +The nodes will be added to DNS so the load can be shared. + +The origin server requirements will be dictated by the needs of the nodes. + +Nodes will monitor a rss control channel from the origin server for instructions. + +The RSS file will contain a link to a json file. + +The json file will contain + +- the fail2ban settings, list of ip addresses, maxretry, findtime and bantime +- force full rsync Boolean flag - false +- hours between rsync eg every 3 hours +- list of origin servers +- list of Admin IP addresses +- list of IP addresses to ban immediately +- list of useragents to ban immediately + +If the origin server is not available then the nodes should connect to the next origin server in the list. + +nodes will synchronize the files from a origin server using rsync over ssh. + +A full rsync is sufficient to ensure files are copied correctly. + +A full rsync will be done several times a day, eg every 3 hours - from settings + +atomic updates are not required as only new files are added. + +There are thousands of files + +The files will come from a well known directory eg `public_html/eps/hpr${ep_num}/hpr${ep_num}*.${extension}` + +The `ep_num` will be a digit from 0001 to 9999 + +The `extension` will be from a well know list from the origin updated perhaps once a year + +nodes will then serve the files using `nginx` + +`nginx` must restrict access to files outside the well known files + +A list of well known files that clients require (favorite icon, robots.txt, etc) will be available from the origin server. + +The files we wish to serve are all Creative Commons Licensed so can be shared. + +Random browsing is not allowed. + +All file paths are known, and are provided by the origin server. + +There should never be any request for a file outside the well known paths. + +Any attempt to access any file outside the known paths should be logged. + +Based on a configuration provided by the origin server, the server will run failtoban + +repeat offenders will be banned for 7 days. + +The Admin IP addresses should never be banned + +traffic should be secured with let's encrypt of equivalent tls transport + +clients can request the files without tls + +The nodes public ip address will be placed on a allow list for ssh + +Monitoring should be available using Prometheus or similar. + + + + + + + + + + + + + + + + +----------------+ + | Origin Server A| + +--------+-------+ + | + RSS Control Feed + | + JSON Configuration + | + +------------------+------------------+ + | | + v v + ++----------------+ +----------------+ +| Edge Node 1 | | Edge Node 2 | +| | | | +| nginx | | nginx | +| rsync | | rsync | +| fail2ban | | fail2ban | +| node-agent | | node-agent | +| prometheus exp.| | prometheus exp.| ++--------+-------+ +--------+-------+ + | | + +----------------+--------------------+ + | + DNS RR + | + Clients + + diff --git a/ccdn/hpr.minisign.1785664421.pubkey b/ccdn/hpr.minisign.1785664421.pubkey new file mode 100644 index 0000000..1c48e80 --- /dev/null +++ b/ccdn/hpr.minisign.1785664421.pubkey @@ -0,0 +1,2 @@ +untrusted comment: minisign public key 85F68B90C4D786B9 +RWS5htfEkIv2hSLt8UJmbUF4MV5P0JX+IcHq354RNF6wgC/bO/M3OxqU